【异或】HDU - 4810 Wall Painting

题目链接

Description

给定n个数,第 i 天选取 i 个数,使这 i 个数相异或,对这一天所有可能的组合 异或后 的值进行求和,输出从1到n天的结果

Solution

首先0和1异或是不会影响结果的,只有1和1异或并且1的个数是奇数的时候才会对结果产生影响,所以考虑将每个数转换成二进制,统计每一位上的1的个数,利用组合数学解决。(参考异或的性质

因此,枚举所有二进制位,计算出每个第 j 位上1的个数 bit[j],枚举 1~k 之间所有的奇数(1,3,5,7……),利用组合数求出各种取法的和然后乘上权值 1 << j。

例如对于一些二进制数,第0位上一共有4个1,第1位上一共有4个1,第2位上一共有0个1,第3位上一共有3个1,如下所示:
3 2 1 0 (二进制位)
3 0 4 4 (1的个数)
结果就是把最后的每一位上的数转换出来 ans1 = 3pow(2,3) + 0pow(2,2)+ 4pow(2,1) + 4pow(2,0) = 36.
那么这个过程转换为公式就是:

a n s [ i ] + = ( C [ b i t [ j ] ] [ k ] ∗ C [ n − b i t [ j ] ] [ i − k ] ) ∗ ( 1 &lt; &lt; j ) ans[ i ] += (C[ bit[j] ][ k ] * C[ n - bit[j] ][ i - k ] ) * ( 1 &lt;&lt; j ) ans[i]+=(C[bit[j]][k]C[nbit[j]][ik])(1<<j)

其中ans[ i ] 代表选取 i 个数的最后结果,C[ bit[j] ][ k ] 为在 bit[j] 个1中选取k个1, k 为奇数,C[ n - bit[j] ][ i - k ] 为在 n-bit[j] 个0中选取 i-k 个0

他们的乘积就是每一位上可能的组合的数目,然后再乘这一位上的值(1 << j)就是这一位上的结果,然后把每一位上的加起来就行了。

Code

#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
#define fi first
#define se second
#define fopen freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout);
#define mst(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;
const double eps = 1e-9;
const int Mod = 1e6 + 3;
const int MaxN = 1e3 + 5;

int c[MaxN][MaxN];
int a[MaxN], bit[MaxN];
int n;

void get_C() {
    mst(c, 0);
    c[0][0] = 1;
    for(int i = 1; i < MaxN; i++) {
        c[i][0] = 1;
        for(int j = 1; j <= i; j++)
            c[i][j] = (c[i-1][j] + c[i-1][j-1]) % Mod;
    }
}

void get_bit() {
    mst(bit, 0);
    for(int i = 1; i <= n; i++) {
        for(int j = 0; j < 32; j++) {
            if(a[i] & (1 << j)) bit[j]++;
        }
    }
}

int main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); 

    get_C();
    while(cin >> n) {
        for(int i = 1; i <= n; i++) cin >> a[i];
        get_bit();
        for(int i = 1; i <= n; i++) {
            LL ans = 0LL;
            for(int j = 0; j < 32; j++) {
                for(int k = 1; k <= bit[j] && k <= i; k += 2) {
                    ans += ((LL)c[bit[j]][k] * c[n-bit[j]][i-k]) % Mod * ((1 << j) % Mod);
                    ans %= Mod;
                }
            }
            i == n ? cout << ans % Mod << endl : cout << ans % Mod << " ";
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值