BITを使ってみた

前に出たCodeChefのコンテストで解けなかった問題を解いてみました。

Magic Board
http://www.codechef.com/problems/MBOARD

N*NのフィールドとQ個のクエリが与えられる。
クエリは
RowSet i x: i行目をx(=0 or 1)にする。
ColSet i x: i列目をx(=0 or 1)にする。
RowQuery i: i行目の0の数を答える。
ColQuery i: i列目の0の数を答える。

N, Q <= 500000なのでシミュレーションは無理。
i行目の0の数を知るには、
・最後のRowSet i xのxが0か1か
・最後のRowSet i xのあとにColSet j (xの逆) が何個発生したか分かれば良い。

全く分かりませんでしたが、BITを使ってタイムスタンプを覚えておけば出来るらしいです。

MBOARD Editorial
http://discuss.codechef.com/questions/6379/mboard-editorial


BITってなんだ??ということで調べてみました。
BITというのはBinary Indexed Treeの略で、Fenwick Treeともいうそうです。例えば長さnのIntの配列が与えられた時にi〜j番目の総和などをO(logn)時間で計算できます。

調べてみると、構造的にはセグメント木のようなイメージですが、実装がものすごく簡単。
なんと、C++でadd、queryが以下の短さで書けます。驚きの短さ!

//配列のindex番目にvalを足す操作
void add(int *ary, int index, int val){
	int t = index;
	if(t == 0) return;
	while(t <= q + 1){
		ary[t] += val;
		t += t&(-t);
	}
}
//配列の先頭からindex番目までの総和を取る操作
int query(int*ary, int index){
	int ret = 0;
	int t = index;
	while(0 < t){
		ret += ary[t];
		t -= t&(-t);
	}
	return ret;
}

詳しくは調べてみてください。t & (-t)でtの最後のビットが取れるのがポイント。あとBITでは0-indexではなくて1-indexの方が簡単な気がします。

参考 : http://spinda2.blog48.fc2.com/blog-entry-427.html


BITを使ってMagic Boardを解いたコードはこちら(http://www.codechef.com/viewsolution/1844022)。
フィールドが始め0になっていますが、これは仮想的に1個目のクエリとし、i回目のクエリをi + 1回目とすると分かりやすいと思います。