题意:给定n个整数,求满足子集异或和为0的子集大小之和。
题解:相当于求每个数出现在子集中的次数之和。
先对n个数求线性基,设线性基大小为r,可以分别计算线性基内数的贡献和线性基外数的贡献
1.线性基外:共n-r个数,枚举每个数x,将线性基外剩余的n-r-1个数任意排列,显然共有 2^{n-r-1}个集合,这些集合再异或x的结果还是能被线性基异或出,所以x的贡献为 2^{n-r-1}。
2.线性基内:枚举每个数x,将所有剩余的n-1个数再求一次线性基,设为B,分两种情况:
(1) x不能被B异或出。那么显然x不能在任意一个集合中出现,x的贡献为0。
(2) x可以被B异或出。此时B的大小必定也为r,因为B已经能表示所有n个数了。那么在除去x和B的情况下,剩余n-r-1个数显然也是任意排列,x贡献为 2^{n-r-1}。
copy的dicsh
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 100000 + 5;
const int mod = 1e9 + 7;
ll a[maxn];
vector<int> ID;
struct LinerBase {
int cnt;
ll b[65];
void init() {
cnt = 0;
memset(b, 0, sizeof(b));
}
bool Insert(ll x)
{
for (int i=60;i>=0;i--)
if (x&(1LL<<i))
{
if (!b[i]){ b[i]=x;cnt++;break; }
else x^=b[i];
}
return x>0;
}
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)) {
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.check(a[i])) {
B1.Insert(a[i]);
ID.push_back(i);
}
else B2.Insert(a[i]);
}
int r = B1.cnt;
if(r == n) {
printf("0\n");
continue;
}
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 < 65; 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);
}
return 0;
}