Codeforces 333D Characteristics of Rectangles

問題

hxwの行列がある。
この行列のうち、(a, b)を左上(c, d)を右下とするような長方形を選ぶ。
角の4つの数字の最小値の最大値を求めよ。

制約条件

h, w≦1000
数字≦10^9

方針

なんか頭悪いことした。
二分探索 + ビット並列化。
二分探索で、行列のうち数字がmid以上であるものを取り出す。
ここに長方形が存在するかを見るのにビット並列化した。


長方形は、bit[i] & bit[j]に1が二つ以上たっているとき存在する。
1000ビットの整数型はないのでlong long16個に分割する。
二分探索は10^9までまわすとだめなので座標圧縮して、存在する数字だけでまわす。


本来の解法は、
同じ列にi, jに同時に1が存在するということをmemo[i][j]にもっておいて、これをまわすという方法。
計算量がぱっと見よくわからないが爆速。

ソースコード

int h, w, a[1000][1000];
ll bit[1000][16];
vi v;

int main(){
	scanf("%d%d", &h, &w);
	rep(i, h) rep(j, w){
		scanf("%d", a[i] + j);
		v.pb(a[i][j]);
	}
	sort(all(v));
	v.erase(unique(all(v)), v.end());
	
	int lo = 0, hi = v.size(), mid;
	while(lo + 1 < hi){
		mid = (lo + hi) / 2;
		memset(bit, 0, sizeof(bit));
		rep(i, h){
			rep(j, w) if(a[i][j] >= v[mid]) bit[i][j / 64] |= 1ull << j % 64;
			rep(j, i){
				int s = 0;
				rep(k, 16) s += __builtin_popcountll(bit[i][k] & bit[j][k]);
				if(s >= 2){
					lo = mid;
					goto NEXT;
				}
			}
		}
		hi = mid;
		NEXT:;
	}
	cout << v[lo] << endl;
	return 0;
}