zoukankan      html  css  js  c++  java
  • 【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
  • 相关阅读:
    【Oracle】将表名与字段名连接成一行数据展示,字段名使用顿号的分隔
    【Accountancy】资产
    【FinacialKnowledge】财务报表及名词解释
    模块独立性原理
    C# this.Invoke()的作用与用法
    浅谈C#委托和事件(转载)
    C#三种定时器的实现
    一张图看懂开源许可协议,开源许可证GPL、BSD、MIT、Mozilla、Apache和LGPL的区别
    c++ comment
    C#使用StreamWriter类写入文件文件
  • 原文地址:https://www.cnblogs.com/cdcq/p/11211836.html
Copyright © 2011-2022 走看看