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 }

 

posted @ 2019-08-13 23:30  Changer-qyz  阅读(216)  评论(0编辑  收藏  举报