题目链接
题意:给定n个整数,求满足子集异或和为0的子集大小之和。
题解:相当于求每个数出现在子集中的次数之和。
先对n个数求线性基,设线性基大小为r,可以分别计算线性基内数的贡献和线性基外数的贡献
-
线性基外:共n-r个数,枚举每个数x,将线性基外剩余的n-r-1个数任意排列,显然共有2n-r-1 个集合,这些集合再异或x的结果还是能被线性基异或出,所以x的贡献为2n-r-1
-
线性基内:枚举每个数x,将所有剩余的n-1个数再求一次线性基,设为B,分两种情况:
(1) x不能被B异或出。那么显然x不能在任意一个集合中出现,x的贡献为0。
(2) x可以被B异或出。此时B的大小必定也为r,因为B已经能表示所有n个数了。那么在除去x和B的情况下,剩余n-r-1个数显然也是任意排列,x贡献为2n-r-1 。
//#pragma comment(linker, "/STACK:102400000,102400000")
#include "bits/stdc++.h"
#define pb push_back
#define ls l,m,now<<1
#define rs m+1,r,now<<1|1
#define hhh printf("hhh\n")
#define see(x) (cerr<<(#x)<<'='<<(x)<<endl)
using namespace std;
typedef long long ll;
typedef pair<int,int> pr;
inline int read() {int x=0;char c=getchar();while(c<'0'||c>'9')c=getchar();while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();return x;}
const int maxn = 1e5+10;
const int mod = 1e9+7;
const double eps = 1e-9;
ll a[maxn];
vector<int> ID; //用ID来存储哪些数能插入到线性基中
struct LinerBase{
int cnt;
ll b[64];
void init() {
cnt=0;
memset(b,0,sizeof(b));
}
bool Insert(ll x) {
for(int i=63; i>=0; --i) {
if(x&(1LL<<i)) {
if(!b[i]) { b[i]=x; cnt++; return true; }
else x^=b[i];
}
}
return false;
}
bool check(ll x) {
for(int i=63; i>=0; --i) {
if(x&(1LL<<i)) x^=b[i];
}
return x==0;
}
}B1, B2, B3;
ll qpow(ll a, ll b) {
ll res=1;
while(b) {
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
int main() {
int n;
while(scanf("%d", &n)!=EOF) {
for(int i=1; i<=n; ++i) scanf("%lld", &a[i]);
B1.init(), B2.init(), ID.clear();
for(int i=1; i<=n; ++i)
if(B1.Insert(a[i])) ID.push_back(i);
else B2.Insert(a[i]);
int r=B1.cnt;
if(r==n) { printf("0\n"); continue; } //全都插入线性基了,表明所有的数互相异或无法构造出0
ll tp=qpow(2,n-r-1);
ll ans=(n-r)*tp%mod;
for(int i=0; i<ID.size(); ++i) {
ll x=a[ID[i]];
for(int j=0; j<64; ++j) B3.b[j]=B2.b[j];
for(int j=0; j<ID.size(); ++j)
if(j!=i) B3.Insert(a[ID[j]]);
if(B3.check(x)) ans=(ans+tp)%mod;
}
printf("%lld\n", ans);
}
}