H.XOR(线性基)
传送门
题目描述
Bobo has a set A of n integers
a
1
,
a
2
,
…
,
a
n
a_1, a_2, \dots, a_n
a1,a2,…,an .He wants to know the sum of sizes for all subsets of A whose xor sum is zero modulo
(
1
0
9
+
7
)
(10^9+7)
(109+7)Formally, find
(
∑
S
⊆
A
,
⊕
x
∈
S
x
=
0
∣
S
∣
)
 
m
o
d
 
(
1
0
9
+
7
)
\left(\sum_{S \subseteq A, \oplus_{x \in S} x = 0} |S|\right) \bmod (10^9+7)
(∑S⊆A,⊕x∈Sx=0∣S∣)mod(109+7).Note that
⊕
\oplus
⊕ denotes the exclusive-or (XOR).
输入描述:
The input consists of several test cases and is terminated by end-of-file.
The first line of each test case contains an integer n.
The second line contains n integers
a
1
,
a
2
,
…
,
a
n
a_1, a_2, \dots, a_n
a1,a2,…,an
1
≤
n
≤
1
0
5
1 \leq n \leq 10^5
1≤n≤105
0
≤
a
i
≤
1
0
18
0 \leq a_i \leq 10^{18}
0≤ai≤1018
The number of test cases does not exceed 100.
The sum of n does not exceed
2
×
1
0
6
2 \times 10^6
2×106
输出描述:
For each test case, print an integer which denotes the result.
示例1
输入
1
0
3
1 2 3
2
1000000000000000000 1000000000000000000
输出
1
3
2
题目大意:
给定一个序列S,问这个序列的所有子序列中,子序列所有的元素的异或和为0的子序列长度和为多少?可以转化问题,求每个元素属于子集数的和,也就是统计每个元素对答案的贡献。
思路:
首先,很明显的异或,用线性基。
线性基有几个性质:
1、基内一定是线性无关的。
2、集合B是S的线性基,则S中的所有元素都可有B中元素异或得到,且只有一种异或方式。
3、某集合S的可以得到长度为r的线性基B(r < n),则如果基中元素可以由另外(n - 1)个元素异或得到,那么会存在另一个不同的长度为r的线性基.
所以这题,我们可以先构建一个线性基,里面含有cnt个元素。
那么其余的
N
−
c
n
t
N-cnt
N−cnt个元素中每个都可以构成
2
N
−
c
n
t
−
1
2^{N-cnt-1}
2N−cnt−1个子串,所以每个的贡献度为
2
N
−
c
n
t
−
1
2^{N-cnt-1}
2N−cnt−1(选一个,然后其他的N-cnt-1随便取)因为是线性基是极大线性无关组,所以N-cnt-1都可以由线性基里的特定元素集表示。
然后我们就看线性集里面元素的贡献度。先将N - cnt 个数求出一个线性基,再将原来基中的cnt-1个数再放入当前的线性基中,剩下的一个数判断它是否能由新得到的基组成, 如果可以那么他的贡献度还是 2 N − c n t − 1 2^{N - cnt- 1} 2N−cnt−1,因为当前的基的长度还是cnt , 还有 n - r个数可以和他组合.(可以构成的话还是极大线性无关组,cnt相同)(性质三)
ac_code
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define LL long long
int n;
LL a[110000];
LL bssr[110],bssb[110],bssd[110];
LL ans=0,mod=1000000007;
bool flg[110000];
LL q[110000],hd=0;
LL cnt=0;
bool add(LL x,LL y[]){ //线性基的插入
for(int i=63;i>=0;--i)
if((LL)1<<i&x){
if(!y[i]){
y[i]=x;
++cnt;
return true;
}
x^=y[i];
}
return false;
}
bool chck(LL x,LL y[]){ //判断是否可以由线性基构成
for(int i=63;i>=0;--i)
if((LL)1<<i&x)
x^=y[i];
return !x;
}
LL power(LL x,int y){ //快速幂
LL ans=1;
while(y){
if(y&1) ans=(ans*x)%mod;
x=(x*x)%mod;
y>>=1;
}
return ans;
}
void init(){ //初始化
memset(bssr,0,sizeof(bssr));
memset(bssb,0,sizeof(bssb));
for(int i=1;i<=n;++i)
flg[i]=false;
ans=0;
hd=0;
cnt=0;
}
int main(){
while(~scanf("%d",&n)){
init();
for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
for(int i=1;i<=n;++i){
flg[i]=add(a[i],bssr);
if(flg[i]) q[++hd]=a[i];
}
if(cnt==n){
printf("0\n");
continue;
}
ans+=(n-cnt)*power(2,n-cnt-1)%mod; //N-cnt个元素的贡献
for(int i=1;i<=n;++i)
if(!flg[i]) //cnt以外的元素构成的线性基
add(a[i],bssb);
for(int k=1;k<=hd;++k){
memset(bssd,0,sizeof(bssd));
cnt=0;
for(int i=1;i<=hd;++i)
if(i!=k)
add(q[i],bssd);
for(int i=0;i<=63;++i)
if(bssb[i])
add(bssb[i],bssd); //n-1的线性基
if(chck(q[k],bssd)) //判断是否可以由线性基得到
ans=(ans+power(2,n-cnt-1))%mod;
}
printf("%lld\n",ans);
}
return 0;
}