TopCoder SRM 545 Div1 Hard SetAndSet
問題
Aを非負の整数列とする。
Aの各要素を赤または青に色づけする。
色づけ後で、それぞれの色に色づけされた要素のAND(ビット毎のand)を
取ったとき、両者が等しくなっているような色づけの仕方は何通りあるか、求めよ。
制約条件
A[i]≦1048575
Aの要素≦50
方針
包除原理で計算する。
各ビットごとに見たとき、
全てのiでビットが0または1であるビットは無視してよい。
0と1が混在するビットは、0が全て同じ色になると、条件を満たさなくなる。
ここで、見るビットの集合Tを決めたときにTの全てで失敗する場合の数をg(T)と置くと、
g(T)を全部計算し、包除原理を使うことで、
ある集合Sに対してどこか一つで失敗する場合の数f(S)が求まる。
g(T)は再帰とunion-findを使いながら求めていく。
あるビットをTに加えるとき、0が二つ立っているビットがあったら、それらの色は同じにしなくてはならないので、その要素同士をマージする。
というのを繰り返した後、
2 ^ (連結成分の個数 + n - 0が登場しなかった要素)
というのがg(T)になっている。
ソースコード
bool v[60]; int n, m, p[30][60]; ll nz[60], ans; void rec(int cur, int use, int k, ll paint){ if(cur == m){ ll tmp = 1ll << k + n - __builtin_popcountll(paint); if(use % 2) ans -= tmp; else ans += tmp; } else{ v[cur] = 1; int nk = k + 1; rep(i, cur) p[cur][i] = p[cur - 1][i]; p[cur][cur] = cur; for(int i = cur - 1; i >= 0; i--){ if(v[i] && (nz[cur] & nz[i])){ if(p[cur][p[cur][i]] != cur) p[cur][p[cur][i]] = cur, nk--; } } for(int i = cur - 1; i >= 0; i--){ p[cur][i] = p[cur][p[cur][i]]; } rec(cur + 1, use + 1, nk, paint | nz[cur]); rep(i, cur) p[cur][i] = p[cur - 1][i]; p[cur][cur] = cur; v[cur] = 0; rec(cur + 1, use, k, paint); } } class SetAndSet { public: long long countandset(vector <int> A) { n = A.size(); m = 0; rep(j, 20){ ll bit = 0; rep(i, n) if(!(A[i] >> j & 1)) bit |= 1ll << i; if(bit != (1ll << n) - 1 && bit) nz[m++] = bit; } if(m == 0) return (1ll << n) - 2; ans = 0; memset(v, 0, sizeof(v)); rec(0, 0, 0, 0); return ans; } };