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 ? '
' : ' ');
}
}
}