Codeforces 461(#263 Div1) D. Appleman and Complicated Task

問題

nxnのグリッドの各マスに'o'か'x'を、次の条件を満たすように入れる。

  • 各マスについて、隣接する4マスのうち、'o'が書かれたマスは偶数個

nxnのうち、k個のマスに入るものがあらかじめ決まっているとき、
残りのマスの埋め方は何通りあるかmod 10^9 + 7で求めよ。

制約条件

n≦10^5
k≦10^5

方針

Editorial見た。
グリッドの状態は、最初の一行を決めればユニークに決まり、
一般的にはa[i][j] = Xor{a[0][k] | k≦i+j≦2(n-1)-k かつ |i-j|≦k かつ k%2=(i+j)%2}
と書けるらしい。
(これはどうやったら発見できるんだろう)


なので、i+jを偶数と奇数の場合にわけると、
a[i][j]に関係するkは、L≦k≦Rであり、a[i][j] = a[0][L] ^ a[0][L+1] ... ^ a[0][R]と書けることになる。


この等式を表現するのはunion-findを使えばよい。
s[x] = Xor{a[0][k] | k<x}とすれば、a[i][j] = 0⇔s[R + 1] = s[L]
蟻本の食物連鎖の問題にある感じで、s[x] = 0, 1に対応する頂点をそれぞれ作って、
s[x] = aとs[y] = bが同時に起こるときにs[x] = aの頂点とs[y] = bの頂点をunionする。


で、等式を全部unionし終えた後、s[x] = 0とs[x] = 1の頂点が同じunionにあったら答えは0.
そうでなかったら、答えは2^(unionの個数/2 - 1)通り。

ソースコード

const int MX = 220000;
int odd[MX], even[MX];
int root(int *p, int x){
	if(x == p[x]) return x;
	return p[x] = root(p, p[x]);
}
bool merge(int *p, int a, int b){
	a = root(p, a); b = root(p, b);
	if(a == b) return 0;
	p[b] = a;
	return 1;
}

int n, q;

int main(){
	scanf("%d%d", &n, &q);
	rep(i, 2 * n + 2){
		odd[i] = i;
		even[i] = i;
	}
	int cmp = n;
	while(q--){
		int x, y; char c;
		scanf("%d%d %c", &y, &x, &c);
		x--; y--;
		
		int L = abs(x - y);
		int R = min(x + y, 2 * (n - 1) - x - y);
		
		int *target = (x + y) % 2 ? odd : even;
		int res = 0;
		
		if(R % 2 != (x + y) % 2) R--;
		L /= 2; R /= 2;
		R++;
		
		if(c == 'o'){
			res |= merge(target, 2 * L, 2 * R + 1);
			res |= merge(target, 2 * L + 1, 2 * R);
		}
		else{
			res |= merge(target, 2 * L, 2 * R);
			res |= merge(target, 2 * L + 1, 2 * R + 1);
		}
		cmp -= res;
	}
	ll ans = 1;
	rep(i, cmp) ans *= 2, ans %= inf + 7;
	rep(i, n){
		if(root(odd, 2 * i) == root(odd, 2 * i + 1)) ans = 0;
		if(root(even, 2 * i) == root(even, 2 * i + 1)) ans = 0;
	}
	cout << ans << endl;
	return 0;
}