【2019牛客多校第一场】XOR
题意:
给你一个集合A,里边有n个正整数,对于所有A的、满足集合内元素异或和为0的子集S,问你∑|S|
n<=1e5,元素<=1e18
首先可以转化问题,不求∑|S|,而是求每个元素属于子集数的和,也就是统计每个元素对答案的贡献
(题解中说根据期望的线性?我不懂期望和这个有啥关系,但是并不影响理解)
既然要求集合中的异或和,线性基就是针对这一类问题的一把好手
先给A求一个基R
对于没有被扔进R的元素,每一个元素对答案的贡献都是2^(n-|R|-1)
因为对于每个元素,先把它选走,剩下的不在R中的元素就可以随便选(也可以都不选),然后基R中一定能找出对应的元素组合把它们搞成0
为什么不用担心R中会有被选出元素相关的贡献没有统计?
注意线性基的性质:基中元素所有子集异或和都不同,保证了不会存在多种从R中选元素的方案,使得和不在R中选的元素异或和为0
接下来需要统计R基中元素对答案的贡献
遍历R中的元素,把它拎出来,给剩下的n-1个元素建个基D
如果被拎出来的元素还能插♂进基D,那就没救了,绝对异或不出0
否则它对答案的贡献就是2^(n-|D|-1),理由同上,不在D中的其他元素可以随便选,而从D中选子集的方案唯一
求基D可以加速,给n个元素中没在基R里的元素建个基B(这名字真鬼 = =)
然后每次把扔掉一个元素的基R和B合并
(线性基最好玩的地方就是用一个小集合代表一个大集合,还能保证不重复不遗漏)
这题我一开始把R中元素拎出来的方法是直接从基R的数组里挑,这种方法是错误的,反例:
7 8 6 8 9 8
如果直接从基里挑会直接把7和6(的第2,3个二进制位)一起挑出来,6本来还可以提供个0110的,但是往基里一放就被7搞没了
正确的姿势应该是开数组把进R基的元素存起来,然后在这个数组里挑出,剩下的重新进基
经验总结:
1.结构化程序设计很重要!之前开了3个数组,然后给每个数组都写个基,debug很难受
用把数组当函数参数的操作就可以只写一套线性基操作,需要用哪个基当参数扔进去即可
2.不要忘记对拍。包括看别人代码找自己错误的时候,有时候眼睛看不出来有啥问题,拍一拍就找到了
写个对拍也没多费事
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 using namespace std; 7 #define LL long long 8 LL rd(){LL z=0,mk=1; char ch=getchar(); 9 while(ch<'0'||ch>'9'){if(ch=='-')mk=-1; ch=getchar();} 10 while(ch>='0'&&ch<='9'){z=(z<<3)+(z<<1)+ch-'0'; ch=getchar();} 11 return z*mk; 12 } 13 int n; LL a[110000]; 14 LL bssr[110],bssb[110],bssd[110]; 15 LL ans=0,mo=1000000007; 16 bool flg[110000]; 17 LL q[110000],hd=0; 18 bool ist(LL x,LL y[]){ 19 for(int i=63;i>=0;--i)if((LL)1<<i&x){ 20 if(!y[i]){ 21 y[i]=x; 22 ++y[64]; 23 return true; 24 } 25 x^=y[i]; 26 } 27 return false; 28 } 29 bool chck(LL x,LL y[]){ 30 for(int i=63;i>=0;--i)if((LL)1<<i&x) 31 x^=y[i]; 32 return !x; 33 } 34 LL qcp(LL x,int y){ 35 LL z=1; 36 for(;y;y>>=1){ 37 if(y&1) z=(z*x)%mo; 38 x=(x*x)%mo; 39 } 40 return z; 41 } 42 void prvs(){ 43 memset(bssr,0,sizeof(bssr)); 44 memset(bssb,0,sizeof(bssb)); 45 for(int i=1;i<=n;++i) flg[i]=false; 46 ans=0; 47 hd=0; 48 return ; 49 } 50 int main(){ 51 //freopen("ddd.in","r",stdin); 52 //freopen("ddd.out","w",stdout); 53 while(~scanf("%d",&n)){ 54 prvs(); 55 for(int i=1;i<=n;++i) a[i]=rd(); 56 for(int i=1;i<=n;++i){ 57 flg[i]=ist(a[i],bssr); 58 if(flg[i]) q[++hd]=a[i]; 59 } 60 if(bssr[64]==n){ 61 printf("0\n"); 62 continue; 63 } 64 ans+=(n-bssr[64])*qcp(2,n-bssr[64]-1)%mo; 65 for(int i=1;i<=n;++i)if(!flg[i]) 66 ist(a[i],bssb); 67 /*for(int k=0;k<=63;++k)if(bssr[k]){ 68 memset(bssd,0,sizeof(bssd)); 69 for(int i=0;i<=63;++i) 70 if(i!=k) bssd[i]=bssr[i]; 71 bssd[64]=bssr[64]-1; 72 for(int i=0;i<=63;++i)if(bssb[i]) 73 ist(bssb[i],bssd); 74 if(chck(bssr[k],bssd)) 75 ans=(ans+qcp(2,n-bssd[64]-1))%mo; 76 }*/ 77 for(int k=1;k<=hd;++k){ 78 memset(bssd,0,sizeof(bssd)); 79 for(int i=1;i<=hd;++i)if(i!=k) 80 ist(q[i],bssd); 81 for(int i=0;i<=63;++i)if(bssb[i]) 82 ist(bssb[i],bssd); 83 if(chck(q[k],bssd)) 84 ans=(ans+qcp(2,n-bssd[64]-1))%mo; 85 } 86 printf("%lld\n",ans); 87 } 88 return 0; 89 }