題意:
給你一個集合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 }