【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 }
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM