题意:

给你一个集合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.不要忘记对拍。包括看别人代码找自己错误的时候,有时候眼睛看不出来有啥问题,拍一拍就找到了

写个对拍也没多费事

代码:

 #include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define LL long long
LL rd(){LL z=,mk=; char ch=getchar();
while(ch<''||ch>''){if(ch=='-')mk=-; ch=getchar();}
while(ch>=''&&ch<=''){z=(z<<)+(z<<)+ch-''; ch=getchar();}
return z*mk;
}
int n; LL a[];
LL bssr[],bssb[],bssd[];
LL ans=,mo=;
bool flg[];
LL q[],hd=;
bool ist(LL x,LL y[]){
for(int i=;i>=;--i)if((LL)<<i&x){
if(!y[i]){
y[i]=x;
++y[];
return true;
}
x^=y[i];
}
return false;
}
bool chck(LL x,LL y[]){
for(int i=;i>=;--i)if((LL)<<i&x)
x^=y[i];
return !x;
}
LL qcp(LL x,int y){
LL z=;
for(;y;y>>=){
if(y&) z=(z*x)%mo;
x=(x*x)%mo;
}
return z;
}
void prvs(){
memset(bssr,,sizeof(bssr));
memset(bssb,,sizeof(bssb));
for(int i=;i<=n;++i) flg[i]=false;
ans=;
hd=;
return ;
}
int main(){
//freopen("ddd.in","r",stdin);
//freopen("ddd.out","w",stdout);
while(~scanf("%d",&n)){
prvs();
for(int i=;i<=n;++i) a[i]=rd();
for(int i=;i<=n;++i){
flg[i]=ist(a[i],bssr);
if(flg[i]) q[++hd]=a[i];
}
if(bssr[]==n){
printf("0\n");
continue;
}
ans+=(n-bssr[])*qcp(,n-bssr[]-)%mo;
for(int i=;i<=n;++i)if(!flg[i])
ist(a[i],bssb);
/*for(int k=0;k<=63;++k)if(bssr[k]){
memset(bssd,0,sizeof(bssd));
for(int i=0;i<=63;++i)
if(i!=k) bssd[i]=bssr[i];
bssd[64]=bssr[64]-1;
for(int i=0;i<=63;++i)if(bssb[i])
ist(bssb[i],bssd);
if(chck(bssr[k],bssd))
ans=(ans+qcp(2,n-bssd[64]-1))%mo;
}*/
for(int k=;k<=hd;++k){
memset(bssd,,sizeof(bssd));
for(int i=;i<=hd;++i)if(i!=k)
ist(q[i],bssd);
for(int i=;i<=;++i)if(bssb[i])
ist(bssb[i],bssd);
if(chck(q[k],bssd))
ans=(ans+qcp(,n-bssd[]-))%mo;
}
printf("%lld\n",ans);
}
return ;
}

【2019牛客多校第一场】XOR的更多相关文章

  1. 2019牛客多校第一场 I Points Division(动态规划+线段树)

    2019牛客多校第一场 I Points Division(动态规划+线段树) 传送门:https://ac.nowcoder.com/acm/contest/881/I 题意: 给你n个点,每个点有 ...

  2. 2019牛客多校第一场E ABBA(DP)题解

    链接:https://ac.nowcoder.com/acm/contest/881/E 来源:牛客网 ABBA 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 524288K,其他语 ...

  3. 2019牛客多校第一场H XOR 线性基模板

    H XOR 题意 给出一组数,求所有满足异或和为0的子集的长度和 分析 n为1e5,所以枚举子集肯定是不可行的,这种时候我们通常要转化成求每一个数的贡献,对于一组数异或和为0.我们考虑使用线性基,对这 ...

  4. 2019牛客多校第一场A-Equivalent Prefixes

    Equivalent Prefixes 传送门 解题思路 先用单调栈求出两个序列中每一个数左边第一个小于自己的数的下标, 存入a[], b[].然后按照1~n的顺序循环,比较 a[i]和b[i]是否相 ...

  5. 2019牛客多校第一场 A.Equivalent Prefixes

    题目描述 Two arrays u and v each with m distinct elements are called equivalent if and only if RMQ(u,l,r ...

  6. 2019 牛客多校第一场 D Parity of Tuples

    题目链接:https://ac.nowcoder.com/acm/contest/881/D 看此博客之前请先参阅吕凯飞的论文<集合幂级数的性质与应用及其快速算法>,论文中很多符号会被本文 ...

  7. 2019牛客多校第一场 E-ABBA(dp)

    ABBA 题目传送门 解题思路 用dp[i][j]来表示前i+j个字符中,有i个A和j个B的合法情况个数.我们可以让前n个A作为AB的A,因为如果我们用后面的A作为AB的A,我们一定也可以让前面的A对 ...

  8. 2019 牛客多校第一场 B Integration

    题目链接:https://ac.nowcoder.com/acm/contest/881/B 题目大意 给定 n 个不同的正整数 ai,求$\frac{1}{\pi}\int_{0}^{\infty} ...

  9. 2019牛客多校第一场E ABBA 贪心 + DP

    题意:问有多少个有(n + m)个A和(n + m)个B的字符串可以凑出n个AB和m个BA. 思路:首先贪心的发现,如果从前往后扫,遇到了一个A,优先把它看成AB的A,B同理.这个贪心策略用邻项交换很 ...

随机推荐

  1. [LeetCode] 535. Encode and Decode TinyURL 编码和解码短网址

    Note: This is a companion problem to the System Design problem: Design TinyURL. TinyURL is a URL sho ...

  2. [05]Go设计模式:建造者模式(Builder Pattern)

    目录 建造者模式 一.简介 二.代码 三:参考资料 建造者模式 一.简介 建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象.这种类型的设计模式属于创建型模式, ...

  3. Linux交换分区内存优化

    swappiness的值的大小对如何使用swap分区是有着很大的联系的.swappiness=0的时候表示最大限度使用物理内存,然后才是 swap空间,swappiness=100的时候表示积极的使用 ...

  4. 如何在一个文件中写多个Vue组件(译-有删改)

    原文地址 Writing multiple Vue components in a single file 在一个文件中编写多个组件是React的模式,其中一些文件包含多个组件. 走开发过程中,有些组 ...

  5. 04 javascirpt基础知识---听课笔记

    1.JavaScript概念 一门客户端脚本语言运行在客户端浏览器中的.每一个浏览器都有JavaScript的解析引擎脚本语言:不需要编译,直接就可以被浏览器解析执行了 * 功能:可以来增强用户和ht ...

  6. MySQL中 while loop repeat 的用法

    -- MySQL中的三中循环 while . loop .repeat 求 1-n 的和 -- 第一种 while 循环 -- 求 1-n 的和 /* while循环语法: while 条件 DO 循 ...

  7. 【C++札记】指针数组和数组指针

    指针数组: 存储指针的数组,数组找那个的每个一元素都是指针 例: int* p1[4],p2[0]是一个指向int类型的指针 char* p2[4],p1[0]是一个指向char类型的指针 数组指针: ...

  8. Javascript获取JSON对象长度

  9. C++学习笔记 之 循环

    循环 循环语句允许我们多次执行一个语句或者语句组.(插入流程图) 循环类型 C++为我们提供的循环类型如下: 循环类型 描述 while循环 当给定条件为真时,重复语句或语句组.它会在执行循环主体之前 ...

  10. wireshark抓包新手教程(win10空包问题)

    首先下载官网的wireshark,下载地址https://www.wireshark.org/ 下载完按照提示一步步安装 安装完打开wireshark,安装中文包 安装之前首先讲一下win10截图工具 ...