HDU-4810 Wall Paiting 位运算 , 组合
HDU-4810 Wall Paiting
题意
给定\(n\) 个数,分别输出\(i\) 个答案,每个答案表示从\(n\) 个数中选择\(C_n^i\) 组数,计算这组数的异或和,再将这\(C_n^i\) 组数的和相加。
分析
由于是异或,各位之间不相互影响,我们考虑每个数每一位对答案的贡献,考虑二进制的第\(i\) 位,如果第\(i\) 位对答案有贡献,也就是\(1\) ,那么这一位一定是由奇数个1异或得到,所以我们其实只要统计第\(i\) 位为\(1\) 的个数,对这个排列组合即可。注意还要乘上\(2^i\) 。
代码
ll C[1005][1005];
int num[35];
void get_C()
{
C[0][0] = 1;
for (int i = 1; i <= 1003; i++)
{
C[i][0] = 1;
for (int j = 1; j <= i; j++)
C[i][j] = C[i - 1][j] + C[i - 1][j - 1], C[i][j] %= MOD;
}
}
int main() {
int n;
get_C();
while (~scanf("%d", &n)) {
memset(num, 0, sizeof num);
for (int i = 0; i < n; i++) {
ll x = readint();
for (int j = 32; j >= 0; j--)
if ((1ll << j) & x) num[j]++;
}
for (int i = 1; i <= n; i++) {
ll sum = 0;
for (int j = 32; j >= 0; j--) {
ll res = 0;
for (int k = 1; k <= i; k += 2) {
res += C[num[j]][k] * C[n - num[j]][i - k] % MOD;
res %= MOD;
}
sum = (ksm(2, (ll)j, MOD) * res % MOD + sum) % MOD;
}
Put(sum);
putchar(i == n ? '\n' : ' ');
}
}
}