题意:给定n个数,分别求从这n个数中取1-n个数的异或的和
思路:把这n个数都分别用二进制表示,然后统计每一位中1的个数,因为是异或,所以只有奇数个1才能得到这一位是1,把从n个数中选几个数转化成从一定数量的1和0中选择,比如:
5
1 6 8 10 2 这组例子
0 0 0 1
0 1 1 0
1 0 0 0
1 0 1 0
0 0 1 0
———
2 1 3 1
如果是从中取3个数,那么等于1*c(1,1)*c(4,2)+2*(c(3,1)*c(2,2)+c(3,3))+4*c(1,1)*c(4,2)+8*c(2,1)*c(3,2)
(必须选取奇数个的1)
这样我们可以枚举1-n的情况,由于总的位数不会超过64,枚举的1的个数也不会超过64,所以复杂度不高,不过要提前预处理c[1000][1000],利用了公式c(n,m)=c(n-1,m)+c(n-1,m-1)
完整代码:
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
ll c[1005][1005];
const ll mod=1e6+3;
void init()///预处理c[1000][1000]
{
c[0][0]=1;
for(ll i=1;i<=1000;i++)
c[i][0]=1;
for(ll i=1;i<=1000;i++)
{
for(ll j=1;j<=i;j++)
{
c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
}
}
}
ll data[64];
ll ans[1005];
int main()
{
init();
ll n;
while(scanf("%lld",&n)!=-1)
{
memset(data,0,sizeof(data));
memset(ans,0,sizeof(ans));
for(ll i=0;i<n;i++)
{
ll a;
scanf("%lld",&a);
ll k=0;
while(a)
{
if(a%2)
data[k]++;
a/=2;
k++;
}
}
for(ll i=1;i<=n;i++)
{
for(ll j=0;j<64;j++)
{
if(data[j]==0) continue;
for(ll k=1;k<=data[j]&&k<=i;k+=2)
ans[i]+=(1<<j)%mod*c[data[j]][k]%mod*c[n-data[j]][i-k]%mod;
}
}
for(ll i=1;i<n;i++)
printf("%lld ",ans[i]%mod);
printf("%lld\n",ans[n]%mod);
}
return 0;
}
本文介绍了一种计算给定整数数组中所有可能子集异或和的方法。通过将每个数转换为二进制并统计每位置1的数量,利用组合数学原理进行计算。此算法适用于总位数不超过64位的情况。
855

被折叠的 条评论
为什么被折叠?



