TopCoder SRM 564 Div1 Hard

問題

xor b[i] = nとなるような数列b[i]は何通りあるか求めよ。
(ただし、b[i]はそれぞれ0≦b[i]≦cards[i]を満たす整数)

制約条件

cardsの要素の数≦50
n≦10^9
b[i]≦10^9

方針

cards[i]が一番大きなiの、b[i]の最上位ビットをどうするかで場合分けする。
一番大きな数の最上位ビットがそもそもnの最上位ビットよりも小さかったら0通り。


最上位ビット(= xとする)を1にするとき、これは、
b[i] = b[i] - x、n = n ^ xとおいたときの元の問題に等しいため、再帰すればいい。
再帰1回につき1のビットが1個減るので、この再帰はN * log(N)回程度で終了する。


最上位ビット(= k番目とする)を0にするとき、
一番大きな数のb[i]を調節すれば、k番目未満のビットは自由に変えられる。


なので、一番大きな数のカード以外で、k番目のビットのxorが、nのk番目のビットに等しくなる場合の数をDPで数えればよい。

ソースコード

const int mod = (int)1e9 + 7;
int msb(int n){
	int i = 0;
	for(; n; i++) n /= 2;
	return i - 1;
}
int dp[60][2];
class DefectiveAddition {
	public:
	int count(vector <int> cards, int n) {
		sort(all(cards));
		int m = cards.size();
		if(cards[m - 1] == 0) return n == 0;
		
		int a = msb(cards[m - 1]), b = msb(n);
		if(b > a) return 0;
		
		cards[m - 1] -= 1 << a;
		int ans = count(cards, n ^ (1 << a));
		cards[m - 1] += 1 << a;
		
		memset(dp, 0, sizeof(dp));
		dp[0][0] = 1;
		rep(i, m - 1) rep(j, 2) if(dp[i][j]){
			int c = msb(cards[i]);
			if(c == a){
				(dp[i + 1][j ^ 1] += dp[i][j] * (cards[i] - (1 << c) + 1ll) % mod) %= mod;
				(dp[i + 1][j] += dp[i][j] * (1ll << c) % mod) %= mod;
			}
			else
				(dp[i + 1][j] += dp[i][j] * (cards[i] + 1ll) % mod) %= mod;
		}
		ans = (ans + dp[m - 1][a == b]) % mod;
		return ans;
	}
};