https://zybuluo.com/ysner/note/1228125
题面
给出一个大小为(n)的非负整数集合({a_1,a_2,...,a_n}),等概率地选取(2^n)个子集中的一个,设(S)为子集中所有数的异或和(overline{s_1s_2s_3...s_h}_{(2)}),求所有情况((s_1+s_2+s_3+...+s_h)^k)之和。
- (20pts nleq20)
- (60pts k=1,2,3,4)
- (100pts nleq100,hleq63)
解析
(20pts)算法
(O(2^n))暴搜。
(60pts)算法
先从(k=1)开始吧。
题目转化为求所有情况(s_1+s_2+s_3+...)之和。
而每位(s)为(1)(对答案有贡献)决定于子集中该位为(1)的数的个数为奇数。
于是我们可以数位分开单独讨论。
假设子集中该位为(1)的数的个数为(tot)。
则选出奇数个的方案数为(C_{tot}^1+C_{tot}^3+C_{tot}^5...)。
由于该位为(0)的数对答案没有影响,可以任选,故方案数还需乘上(2^{n-tot}),即为该位对答案的贡献。
所有位贡献相加即可。
处理(k=4)需要化式子:
[sum(s_1+s_2+s_3+...+s_h)^4
]
[=sum(s_1+s_2+...)*(s_1+s_2+...)*(s_1+s_2+...)*(s_1+s_2+...)
]
[=sumsum_{a=1}^hsum_{b=1}^hsum_{c=1}^hsum_{d=1}^hs_as_bs_cs_d
]
于是枚举(a,b,c,d),统计(s_a,s_b,s_c,s_d)四位都不为(0)的数的个数,组合数处理(当然要取奇数个)即可。
复杂度(O(h^kn))。(k=4)时会达到(O(10^9))。
有没有发现枚举(a=2,b=1,c=1,d=1)特别傻逼?这种情况和(a=1,b=1,c=1,d=2)一模一样。
我们强制(a,b,c,d)不降,然后乘上系数即可。(系数计算机爆算即可)
这样复杂度会除(4*3*2*1=24),刚好能过。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define re register
#define il inline
#define ll unsigned long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define fp(i,a,b) for(re int i=a;i<=b;i++)
#define fq(i,a,b) for(re int i=a;i>=b;i--)
using namespace std;
const int N=300,mod=998244853;
ll n,k,num[N],s[70][70][70][70],C[200][200],jc[200],ans;
il ll gi()
{
re ll x=0,t=1;
re char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') t=-1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
return x*t;
}
il void check(re int *a)
{
sort(a+1,a+5);
s[a[1]][a[2]][a[3]][a[4]]++;
}
il void Pre()
{
re int p[5];
C[0][0]=1;
fp(i,1,n)
{
C[i][0]=1;
fp(j,1,n) C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
}
jc[0]=1;fp(i,1,n) jc[i]=(jc[i-1]<<1)%mod;
fp(a,0,63) fp(b,0,63) fp(c,0,63) fp(d,0,63) p[1]=a,p[2]=b,p[3]=c,p[4]=d,check(p);
}
int main()
{
n=gi();k=gi();
fp(i,1,n) num[i]=gi();
Pre();
fp(a,0,63)
fp(b,a,63)
fp(c,b,63)
fp(d,c,63)
{
re ll tot=0,sum=0;
fp(i,1,n)
if((num[i]&(1ll<<a))&&(num[i]&(1ll<<b))&&(num[i]&(1ll<<c))&&(num[i]&(1ll<<d))) ++tot;
for(re int i=1;i<=tot;i+=2) (sum+=C[tot][i])%=mod;
sum=sum*jc[n-tot]%mod*s[a][b][c][d]%mod;
(ans+=sum)%=mod;
}
printf("%lld
",ans);
return 0;
}