TopCoder SRM 602 Div1 Medium PilingRectsDiv1

問題

長方形の紙が2*n枚あり、それぞれの一辺の長さはx[i], y[i]である。
(縦横を入れ替えてもよい)
この紙をn枚ずつに自由にわける。


n枚について次のような得点を考える。
xy平面上に辺が軸に平行になるよう自由に置く。
全ての紙が重なっている領域の面積をn枚の得点とする。


全体の得点は、二つの得点の和である。
得られる全体の得点の最大値を求めよ。

制約条件

n≦10万
一辺≦10億
x[i], y[i]は線形合同法の乱数で生成する

方針

短辺が一番短い紙が入るほうをA群とする。
残りをB群とする。B郡のうち、短辺が一番短い紙をiとする。
iを全通り、効率よく試す。


以下x[.]を短辺、y[.]を長辺とする。
まず、iを決めたとき、B群候補は短辺がx[i]以上の長さであるものに限られる。
候補のうち、長辺が最も短いものをCとする。


CをA群に入れるかB群に入れるかだけ決めれば、後の分け方はgreedyで良い。
CをA群に入れるとき、B群には候補のうち長辺が長いものから順にn-1個を選べばいい。
CをB群に入れるとき、B群には候補のうち長辺が短いものから順にn-1個を選べばいい。


候補のうちからk番目を求めるには、binary indexed treeその他適当なデータ構造を使えばO(logn)以下とかで出来る。
それで、データ構造に適当にデータを追加/削除しながら順にiを試していけばよい。

ソースコード

const int MX = 262144;
int bit[MX];
int sum(int i){
	int res = 0;
	for(; i; i -= i & -i) res += bit[i];
	return res;
}
void add(int i, int x){
	for(; i < MX; i += i & -i) bit[i] += x;
}
int find(int k){
	int lo = 0, hi = MX, mid;
	while(lo + 1 < hi){
		mid = (lo + hi) / 2;
		if(bit[mid] >= k) hi = mid;
		else lo = mid, k -= bit[mid];
	}
	return hi;
}

class PilingRectsDiv1 {
	public:
	long long getmax(int n, vector <int> XS, vector <int> YS, int XA, int XB, int XC, int YA, int YB, int YC) {
		
		memset(bit, 0, sizeof(bit));
		
		vi x = XS, y = YS;
		int m = x.size();
		vector<pi> xy;
		map<pi, int> cnt;
		vi vy;
		ll res = 0;
		
		for(int i = m; i < 2 * n; i++){
			x.pb(((ll)x[i - 1] * XA + XB) % XC + 1);
			y.pb(((ll)y[i - 1] * YA + YB) % YC + 1);
		}
		rep(i, 2 * n){
			 if(x[i] > y[i]) swap(x[i], y[i]);
			xy.pb(mp(x[i], y[i]));
			cnt[mp(y[i], x[i])]++;
		}
		vy = y;
		sort(all(vy));
		sort(all(xy));
		
		if(n == 1) return (ll)x[0] * y[0] + (ll)x[1] * y[1];
		
		for(int i = 2 * n - 1; i > 0; i--){
			
			int y = xy[i].second, x = xy[i].first;
			y = lower_bound(all(vy), y) - vy.begin();
			y = MX - 5 - y;
			
			if(--cnt[mp(xy[i].second, xy[i].first)] == 0)
				cnt.erase(mp(xy[i].second, xy[i].first));
			
			int bb = 2 * n - i - 1; //B郡候補の個数
			ll ax = xy[0].first, bx = xy[i].first;
			
			//minをAに入れる
			if(bb >= n){
				int ay = min(cnt.begin()->first.first, xy[0].second), by = xy[i].second;
				ay = min(ay, vy[MX - 5 - find(bb)]);
				by = min(by, vy[MX - 5 - find(n - 1)]);
				res = max(res, ax * ay + bx * by);
			}
			//minをBに入れる
			if(bb >= n - 1){
				int ay = min(cnt.begin()->first.first, xy[0].second), by = xy[i].second;
				if(bb > n - 1) ay = min(ay, vy[MX - 5 - find(bb - (n - 1))]);
				by = min(by, vy[MX - 5 - find(bb)]);
				res = max(res, ax * ay + bx * by);
			}
			add(y, 1);
		}
		
		return res;
	}
};