2019牛客多校第一场 H.XOR
传送: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 }