传送:https://ac.nowcoder.com/acm/contest/881/H
题意:
给定一个长度为$n$的数列,询问所有异或和为0的子集大小的和。
数据范围:
$1<=n<=10^5,1<=a_i<=10^{18}$。
分析:
首先先考虑暴力做法,需要枚举所有的子集情况。然后求异或和。(但这样肯定tle。
考虑每一个数对于整个答案的贡献。
已知:$X xor X=0$,所以如果一个数如果能被其他几个数表示,那么这个集合的异或和为0。
首先,先求出$n$个数的线性基$LB1$。设线性基的秩为$r$,就是插入线性基内数的个数。
1)考虑线性基外的数的贡献:线性基外的数有$n-r$个,对于一个$LB1$外的数$x$,有剩下的$n-r-1$个数可以与$x$组成$2^{n-r-1}$个子集。这些子集与线性基$LB1$都可构成异或和为0的集合。
2)考虑线性基内的数的贡献:对于$LB1$内的数$x$,可以考虑用剩下的$n-1$个数构成的线性基$LB3$(秩为$r2$),然后判断$x$是否可以插入$LB3$内,如果不可以被插入,那么就是可以由$LB3$内的数异或构成,对于答案的贡献$2^{n-r2-1}$。
1 #include<bits/stdc++.h>
2 using namespace std;
3 typedef long long ll;
4 const int maxn=1e5+10;
5 const ll mod=1e9+7;
6 ll a[maxn]; vector<ll> kk;
7 struct Linear_basis{
8 ll b[65]; int num;
9 int insert(ll x){
10 for (int i=62;i>=0;i--){
11 if (x&(1ll<<i)){
12 if (!b[i]){
13 b[i]=x; num++; break;
14 }
15 x^=b[i];
16 }
17 }
18 return x>0; //是否可插入
19 //true 可插入
20 }
21 int checkin(ll x){
22 for (int i=62;i>=0;i--){
23 if (x&(1ll<<i)){
24 if (!b[i]) break;
25 x^=b[i];
26 }
27 }
28 return x>0; //是否可插入
29 }
30 void clear(){
31 memset(b,0,sizeof(b)); num=0;
32 }
33 }LB1,LB2,LB3;
34 ll pow_(int x,int y){
35 ll res=1ll,base=1ll*x;
36 while (y){
37 if (y&1) (res*=base)%=mod;
38 (base*=base)%=mod;
39 y>>=1;
40 }
41 return res;
42 }
43 int main(){
44 int n;
45 while (~scanf("%d",&n)){
46 LB1.clear();LB2.clear();LB3.clear(); kk.clear();
47 for (int i=1;i<=n;i++){
48 scanf("%lld",&a[i]);
49 int f=LB1.insert(a[i]);
50 if (f) kk.push_back(a[i]);
51 else LB2.insert(a[i]);
52 }
53 int r=LB1.num;
54 if (n==r){ //所有数都被插入线性基
55 printf("0\n");
56 continue;
57 }
58 ll ans=1ll*(n-r)*pow_(2,n-r-1)%mod;
59 for (int i=0;i<kk.size();i++){
60 LB3=LB2;
61 for (int j=0;j<kk.size();j++){
62 if (kk[i]==kk[j]) continue;
63 LB3.insert(kk[j]);
64 }
65 int r2=LB3.num;
66 int f=LB3.checkin(kk[i]);
67 if (!f) (ans+=pow_(2,n-r2-1))%=mod;
68 }
69 printf("%lld\n",ans);
70 }
71 return 0;
72 }