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++14 (judge verdict: Time Limit Exceeded)

#include <iostream>
#include <fstream>
#include <stdio.h>
#include <math.h>
#include <time.h>
#include <string>
#include <tuple>
#include <vector>
#include <map>
#include <unordered_map>
#include <list>
#include <set>
#include <stack>
#include <queue>
#include <cstdlib>
#include <algorithm>
#include <random>
#include <cassert>
using namespace std;
#define LL long long
#define MP(a, b) make_pair(a, b)
#define MMP(a, b, c) make_pair(make_pair(a, b), c)
#define MAX 1000000000
#undef INT_MIN
#undef INT_MAX
#define INT_MIN -2147483648
#define INT_MAX 2147483647
#define LL_MIN (LL)-9223372036854775807
#define LL_MAX (LL)9223372036854775807
#define PI 3.14159265359

//最大値 セグメント木
struct SegmentTree{
private:
	vector<LL> nodes;
	int n;
public:
	void reset(int N){ //初期化する O(N)
		nodes.clear();
		n = 1;
		while(n < N) n *= 2;
		n = 2 * n -1;
		for(int i=0; i<n; i++) nodes.push_back(LL_MIN);
	}
	void update(int i, LL x){ //値を変更する O(log N)
		i += n / 2;
		nodes[i] = x;
		while(i > 0){
			i = (i-1)/2; //親の取得は(i-1)/2
			nodes[i] = max(nodes[i*2+1], nodes[i*2+2]); //子の取得はi*2+1,i*2+2
		}
	}
	LL maximum(int a, int b){ //[a,b)の最大値を求める O(log N)
		return query(a, b, 0, 0, n/2+1);
	}
private:
	LL query(int a, int b, int k, int l, int r){
		if(r <= a || b <= l) return LL_MIN; //交差する場合
		if(a <= l && r <= b) return nodes[k]; //完全に含む場合
		LL valueL = query(a, b, k*2+1, l, (l+r)/2);
		LL valueR = query(a, b, k*2+2, (l+r)/2, r);
		return max(valueL, valueR);
	}
};

int N;
LL D;
LL X[30],Y[30];
SegmentTree st;
LL ans;

vector<pair<LL,LL> > mae; //X_b-X_a , Y_b-X_b
void mae_search(int now, LL f, LL s){
	if(now == N/2){
		mae.push_back(MP(f,s));
		return;
	}
	mae_search(now+1, f, s);
	mae_search(now+1, f-X[now], s-Y[now]);
	mae_search(now+1, f+X[now], s+Y[now]);
}

void ato_search(int now, LL f, LL s){
	if(now == N){
		/*int a = lower_bound(mae.begin(), mae.end(), MP(-D-f, LL_MIN) ) - mae.begin();
		int b = upper_bound(mae.begin(), mae.end(), MP(D-f, LL_MAX) ) - mae.begin();
		ans = max(ans, st.maximum(a, b));*/
		auto a = lower_bound(mae.begin(), mae.end(), MP(-D-f, LL_MIN) );
		auto b = upper_bound(mae.begin(), mae.end(), MP(D-f, LL_MAX) );
		for(auto itr=a; itr!=b; itr++){
			ans = max(ans, s+itr->second);
		}
		return;
	}
	ato_search(now+1, f, s);
	ato_search(now+1, f-X[now], s-Y[now]);
	ato_search(now+1, f+X[now], s+Y[now]);
}

int main() {
	iostream::sync_with_stdio(false);
	
	cin >> N >> D;
	for(int i=0; i<N; i++) cin >> X[i] >> Y[i];

	mae_search(0, 0, 0);
	sort(mae.begin(), mae.end());
	st.reset(mae.size());
	for(int i=0; i<mae.size(); i++){
		st.update(i, mae[i].second);
	}

	ans = 0;
	ato_search(N/2, 0, 0);

	cout << ans << endl;

	return 0;
}