利用期望的线性性,等价于计算每个数在集合中的方案数
线性基。先建立一个线性空间b,假设会的到r个基底,这r个基底是线性无关的(就是说,r个基底的数值无论怎么取,异或和都无法得到零),而剩下的n-r个数都可通过这r个基底之间异或得到。
要满足异或和等于0的话,就必须从n-r个数里面考虑。
对于这n-r个数,无论怎么取得到的异或和,在r个数中肯定有且只有一种组合方式使得这种组合方式的异或和等于n-r中取得的异或和。假设一个数必须在方案中,剩下的数有
2
n
−
r
−
1
2^{n-r-1}
2n−r−1种取法,所以
a
n
s
=
n
∗
(
2
n
−
r
−
1
)
ans = n * (2^{n-r-1})
ans=n∗(2n−r−1)种取法。
对于r个基底,n-r个数中可能有可以代替它作为基底的数字。所以对构成r的数值枚举进行判断。(就类似于r个基底中的一个数跟n-r中的数换了一下,那么这个基底也有
2
n
−
r
−
1
2^{n-r-1}
2n−r−1种方案数)。判断就是根据就是这个基底能不能由其他向量的线性组合出来。如果能就要加上
2
n
−
r
−
1
2^{n-r-1}
2n−r−1种方案数。
所以每次都用n-r个数和r-1个其他基底构建线性空间,看看枚举的这个基底能否insert到线性空间中。
能就表示该数没有其他数与它线性相关,不能就表示可以由其他向量线性组合出来(就是可以跟n-r个数中的一个进行交换)。
下面是口胡,自己都不知道在说什么
进行枚举的时候枚举的是原来的数值而不是线性空间的基底数值。基底数值有点类似于正交分解后的基向量(比如这道题
2
≤
1
0
18
2\leq10^{18}
2≤1018,转换成二进制一共有61位,每个基底提供的是最高位上的1,有61个向量的话就相当于构成61维的线性空间)所以枚举b空间里的基向量会有问题。口胡结束,希望大佬来指出错误,雾
为了方便,我们先用n-r个数组构建一个线性空间b1,然后枚举基底的时候,就可以在b1上面尝试添加b空间的基向量就可以了
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod = 1e9+7;
ll qpow(int a,int x)
{
ll t = a;
a = 1;
while (x)
{
if (x & 1) a = a * t % mod;
t = t * t % mod;
x = x >> 1;
}
return a;
}
struct Basis{
ll basis[61] = {};
int index[61] = {};
bool insert(ll x,int num)
{
for (int i = 60;i>=0;i--)
{
if (x & (1ll<<i))
{
if (basis[i] == 0)
{
basis[i] = x;
index[i] = num;
return true;
}
else x = x ^basis[i];
}
}
return false;
}
};
int main()
{
ll n;
while (scanf("%lld",&n) != EOF)
{
ll a[100010] = {},count = 0;
Basis b;
for (int i = 1;i<=n;i++)
{
scanf("%lld",&a[i]);
if (b.insert(a[i],i) == false) count++;
}
ll t = qpow(2,count - 1);
ll ans = count * t % mod;
ll a1[100010] = {},len = 0;
for (int i = 0;i<=60;i++)
{
if (b.index[i] != 0)
{
a1[++len] = a[b.index[i]];
a[b.index[i]] = 0;
}
}
Basis b1;
for (int i = 1;i<=n;i++) b1.insert(a[i],i);
for (int i = 1;i<=len;i++)
{
Basis b2 = b1;
for (int j = 1;j<=len;j++)
{
if (i != j) b2.insert(a1[j],j);
}
if (b2.insert(a1[i],i) == false) ans = (ans + t) % mod;
}
printf("%lld\n",ans);
}
}