Typical DP Contest S - マス目

問題

幅w高さhのグリッドを、白黒に塗る。
左上および右下のマスは黒であり、左上から右下へ隣接する(辺を共有する)黒い正方形をたどっていけるような塗り方は何通りあるかmod 10^9 + 7で求めよ。

制約条件

w≦6
h≦100

方針

接続関係をもつDP.
dp[i][j]をi行目まで見たとき、接続関係がvectorのjで表されるときの塗り方の場合の数として、これを1行ずつ一気に塗って更新していく。


接続関係は「何列目と何列目が同一の連結成分にあるか」を数字で持てばよくて、
たとえば1, 3, 5がつながっている、2と6がつながっているときは
(1, 2, 1, 3, 1, 2)のようなベクトルを接続関係のベクトルとすればよい。
接続関係のベクトルを更新するときは、ナイーブにunion-findなんかを使ってつながっている頂点をマージし、その後座標圧縮するみたいなことを毎回やって間に合う。

ソースコード

const int mod = (int)1e9 + 7;
int h, w;
map<vi, int> dp[2];
 
int p[12];
int root(int x){
	return x == p[x] ? x : (p[x] = root(p[x]));
}
void merge(int a, int b){
	a = root(a); b = root(b);
	if(a > b) swap(a, b);
	if(a != b) p[b] = a;
}
 
int main(){
	cin >> h >> w;
	int cur = 0, next = 1;
	
	rep(i, 1 << h) if(i & 1){
		vi v(h);
		int c = 1;
		rep(j, h) if(i & 1 << j) v[j] = i && (i & 1 << j-1) ? v[j-1] : c++;
		dp[0][v] = 1;
	}
	
	rep(i, w-1){
		dp[next].clear();
		each(j, dp[cur]) rep(k, 1 << h){
			
			int c = h, to[12];
			const vi &v = j->first;
			vi nv(h);
			
			rep(l, 2*h) p[l] = l, to[l] = -1;
			rep(l, h){
				if(k & 1 << l) nv[l] = c++;
				if(l && (k & 1 << l) && (k & 1 << l-1)) merge(nv[l-1], nv[l]);
				if(v[l] && nv[l]) merge(v[l], nv[l]);
			}
			
			c = 2;
			to[0] = 0; to[1] = 1;
			rep(l, h) if(nv[l]){
				nv[l] = root(nv[l]);
				if(to[nv[l]] < 0) to[nv[l]] = c++;
				nv[l] = to[nv[l]];
			}
			
			if(to[root(1)] < 0) continue;
			(dp[next][nv] += j->second) %= mod;
		}
		swap(cur, next);
	}
	
	ll ans = 0;
	each(i, dp[cur]) if((i->first)[h-1] == 1)
		ans += i->second;
	cout << ans % mod << endl;
	
	return 0;
}