cdcq

梦幻小鱼干

导航

【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

 

posted on 2019-07-19 10:41  cdcq  阅读(947)  评论(2编辑  收藏  举报