Problem:
財宝 (Treasures)

問題

盗賊の Anna と Bruno は大富豪の邸宅に忍び込み，財宝 1 から財宝 N までの N 個の財宝を見つけた．これらの財宝を Anna と Bruno で分配することになった．財宝のうちのいくつかを Anna が取り，残りの財宝のうちのいくつかを Bruno が取る．同じ財宝を 2 人が取ることはできない．Anna や Bruno は財宝を 1 個も取らなくてもよい．また，残った財宝は邸宅に残しておくので，2 人とも取らない財宝があってもよい．

それぞれの財宝には「市場価値」と「貴重度」という 2 つの値が定まっている．Anna が取った財宝の市場価値の合計と Bruno が取った財宝の市場価値の合計の差の絶対値が D 以下なら，Anna は公平だと思って満足する．一方，Bruno は，Anna より貴重度の大きい財宝が欲しいと思っている．

Anna が満足するように財宝を分配したときの，Bruno が取った財宝の貴重度の合計から Anna が取った財宝の貴重度の合計を引いた値の最大値を求めよ．

入力

入力は 1 + N 行からなる．

1 行目には，2 つの整数 N, D (1 ≤ N ≤ 30，0 ≤ D ≤ 1015) が空白を区切りとして書かれている．これは，財宝の個数が N 個であり，Anna が取った財宝の市場価値の合計と Bruno が取った財宝の市場価値の合計の差の絶対値が D 以下なら Anna が満足することを表す．

続く N 行のうちの i 行目 (1 ≤ i ≤ N) には，2 つの整数 Xi, Yi (0 ≤ Xi ≤ 1015，0 ≤ Yi ≤ 1015) が空白を区切りとして書かれている．これは，財宝 i の市場価値が Xi であり，貴重度が Yi であることを表す．

与えられる 5 つの入力データのうち，入力 1 では N ≦ 10 を満たす．また，入力 2 では D = 0 を満たす．

出力

Anna が満足するように財宝を分配したときの，Bruno が取った財宝の貴重度の合計から Anna が取った財宝の貴重度の合計を引いた値の最大値を 1 行で出力せよ．

入出力例

入力例 1

6 15
50 900
30 200
40 100
80 600
60 100
70 700

出力例 1

1200

入力例 2

5 0
0 1000000000000000
0 1000000000000000
1 1
1000000000000000 0
1000000000000000 0

出力例 2

2000000000000000

入出力例 1 においては，Anna が財宝 2 と財宝 3 と財宝 5 を取り，Bruno が財宝 1 と財宝 6 を取ると，取った財宝の市場価値の合計は Anna が 130，Bruno が 120 となり，差の絶対値 10 が D = 15 以下なので Anna は満足する．このとき，取った財宝の貴重度の合計は Anna が 400，Bruno が 1600 となり，Bruno が取った財宝の貴重度の合計から Anna が取った財宝の貴重度の合計を引いた値は 1200 となる．これが最大である．

問題文と自動審判に使われるデータは、情報オリンピック日本委員会が作成し公開している問題文と採点用テストデータです。

Solution in C++11 (judge verdict: Time Limit Exceeded)

#include <limits>
#include <vector>
using namespace std;
template <typename Type> class RMQ
{
private:

	int size;

	vector<Type> maxvalue;

	Type _query(int a, int b, int k, int l, int r)
	{
		if (r <= a || b <= l) { return INF; }
		if (a <= l && r <= b) { return maxvalue[k]; }

		Type vl = _query(a, b, 2 * k, l, (l + r) / 2);
		Type vr = _query(a, b, 2 * k + 1, (l + r) / 2, r);

		return max(vl, vr);
	}

public:

	Type INF;

	RMQ(int size_)
	{
		INF = numeric_limits<Type>::min() / 2;

		for (size = 1; size < size_;) { size <<= 1; }

		maxvalue = vector<Type>(size * 2, INF);
	}

	void update(int i, const Type& x)
	{
		i += size;

		maxvalue[i] = x;

		while (i > 1)
		{
			i = i / 2;

			maxvalue[i] = max(maxvalue[2 * i], maxvalue[2 * i + 1]);
		}
	}

	Type query(int s, int t)
	{
		return _query(s, t, 1, 0, size);
	}
};

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>

#define MAX_N 14348907

long long x_1[MAX_N];
long long y_1[MAX_N];
pair<long long, long long>x2[MAX_N];
long long y2[MAX_N];
RMQ<long long> r(14348907);

int main() {
	long long x[15][2], y[15][2], d;
	int power[16] = { 1,3,9,27,81,243,729,2187,6561,19683,59049,177147,531441,1594323,4782969,14348907 };
	int n, e, f, g, h;
	int mid;
	long long int MAX = 0;
	int a[15];

	memset(x_1, 0, sizeof(x_1));
	memset(y_1, 0, sizeof(y_1));

	cin >> n >> d;
	mid = n / 2;

	for (int i = 0; i < mid; i++) {
		cin >> x[i][0] >> y[i][0];
	}
	for (int i = mid; i < n; i++) {
		cin >> x[i - mid][1] >> y[i - mid][1];
	}

	for (int i = 0; i < power[mid]; i++) {
		for (int j = 0; j < mid; j++) {
			a[j] = (i / power[j]) % 3;
			if (a[j] == 0) {
				x_1[i] -= x[j][0];
				y_1[i] -= y[j][0];
			}
			if (a[j] == 1) {
				x_1[i] += x[j][0];
				y_1[i] += y[j][0];
			}
		}
	}

	for (int i = 0; i < power[n - mid]; i++) {
		for (int j = 0; j < n - mid; j++) {
			a[j] = (i / power[j]) % 3;
			if (a[j] == 0) {
				x2[i].first -= x[j][1];
				x2[i].second -= y[j][1];
			}
			if (a[j] == 1) {
				x2[i].first += x[j][1];
				x2[i].second += y[j][1];
			}
		}
	}

	sort(x2, x2 + power[n - mid]);

	for (int i = 0; i < power[n - mid]; i++) {
		y2[i] = x2[i].first;
		r.update(i, x2[i].second);
	}

	g = min(power[mid], 10000000);
	h = max(power[mid] / 20, 4500000);

	if (n == 30) {
		for (int i = h; i < power[mid]; i++) {
			e = distance(y2, lower_bound(y2, y2 + power[n - mid], -x_1[i] - d));
			f = distance(y2, upper_bound(y2, y2 + power[n - mid], -x_1[i] + d));
			MAX = max(MAX, r.query(e, f) + y_1[i]);
		}
	}
	else {
		for (int i = 0; i < g; i++) {
			e = distance(y2, lower_bound(y2, y2 + power[n - mid], -x_1[i] - d));
			f = distance(y2, upper_bound(y2, y2 + power[n - mid], -x_1[i] + d));
			MAX = max(MAX, r.query(e, f) + y_1[i]);
		}
	}

	cout << MAX << endl;

	return 0;
}