TopCode SRM 601 Div1 Medium WinterAndSnowmen

問題

{1, 2, ..., N}の部分集合Aを選ぶ
{1, 2, ..., M}の部分集合Bを選ぶ。

このとき、AとBの積集合が空かつ、(Aの要素全てのxor)<(Bの要素全てのxor)となる集合の選び方は何通りあるか。
mod 10^9 + 7で求めよ。

制約条件

N, M≦2000


方針

Aの集合のxorとBの集合のxorがはじめて異なる桁を固定する。これをkとする。


1から順に数字をAまたはBに割り振っていくメモ化再帰をする。
rec(いくつまで見たか, Aのk桁目のbit, Bのk桁目のbit, k桁より上の部分のbitのAとB全てのxor)
として計算すればいい。


自分がやったのはなんかセンスない方針。
桁を固定するのは同じなんだけど、1〜min(n, m)の部分とmin(n, m) + 1〜max(n, m)の部分に分けてDPするとかいうことをやってしまった。

ソースコード

const int mod = 1e9 + 7;

int calc(int dp[2][2][2048], int pos, int n){
	int cur = 0, next = 1;
	memset(dp, 0, sizeof(int)*2*2*2048);
	dp[0][0][0] = 1;
	
	for(int i = 1; i <= n; i++){
		memset(dp[next], 0, sizeof(int)*2*2048);
		rep(j, 2) rep(k, 2048) if(dp[cur][j][k]){
			
			if((i & k) >> pos & 1){
				if(k >> pos & 1){
					(dp[next][0][k ^ i] += dp[cur][j][k]) %= mod;
					(dp[next][1][k ^ i] += dp[cur][j][k]) %= mod;
				}
				else{
					(dp[next][0][k ^ i] += dp[cur][j][k] * 2 % mod) %= mod;
				}
			}
			else{
				(dp[next][j][k ^ i] += dp[cur][j][k] * 2 % mod) %= mod;
			}
			(dp[next][j][k] += dp[cur][j][k]) %= mod;
		}
		swap(cur, next);
	}
	return cur;
}

int solve(int n, int m){
	ll res = 0;
	int cur = 0, next = 1;
	static int dp[2][2048];
	static int dp2[2][2][2048];
	
	memset(dp, 0, sizeof(dp));
	dp[0][0] = 1;
	for(int i = min(n, m) + 1; i <= n || i <= m; i++){
		memset(dp[next], 0, sizeof(dp[next]));
		rep(j, 2048){
			(dp[next][j ^ i] += dp[cur][j]) %= mod;
			(dp[next][j] += dp[cur][j]) %= mod;
		}
		swap(cur, next);
	}
	
	rep(pos, 11){
		int p = calc(dp2, pos, min(n, m));
		
		rep(i, 2048) if(dp[cur][i]) rep(j, 2) rep(k, 2048) if(dp2[p][j][k]){
			
			if(!((i ^ k) >> pos & 1)) continue;
			if((i ^ k) >> pos + 1) continue;
			
			if(k >> pos & 1){
				res += dp2[p][j][k] * (ll)dp[cur][i] % mod * (mod / 2 + 1) % mod;
				res %= mod;
			} 
			else{
				if(j == (n < m)) continue;
				res += dp2[p][j][k] * (ll)dp[cur][i] % mod;
				res %= mod;
			}
		}
	}
	
	return res;
}

class WinterAndSnowmen {
	public:
	int getNumber(int N, int M) {
		
		return solve(N, M);
	}
};