H - XOR 线性基

在这里插入图片描述在这里插入图片描述
利用期望的线性性,等价于计算每个数在集合中的方案数
线性基。先建立一个线性空间b,假设会的到r个基底,这r个基底是线性无关的(就是说,r个基底的数值无论怎么取,异或和都无法得到零),而剩下的n-r个数都可通过这r个基底之间异或得到。
要满足异或和等于0的话,就必须从n-r个数里面考虑。
对于这n-r个数,无论怎么取得到的异或和,在r个数中肯定有且只有一种组合方式使得这种组合方式的异或和等于n-r中取得的异或和。假设一个数必须在方案中,剩下的数有 2 n − r − 1 2^{n-r-1} 2nr1种取法,所以 a n s = n ∗ ( 2 n − r − 1 ) ans = n * (2^{n-r-1}) ans=n(2nr1)种取法。
对于r个基底,n-r个数中可能有可以代替它作为基底的数字。所以对构成r的数值枚举进行判断。(就类似于r个基底中的一个数跟n-r中的数换了一下,那么这个基底也有 2 n − r − 1 2^{n-r-1} 2nr1种方案数)。判断就是根据就是这个基底能不能由其他向量的线性组合出来。如果能就要加上 2 n − r − 1 2^{n-r-1} 2nr1种方案数。
所以每次都用n-r个数和r-1个其他基底构建线性空间,看看枚举的这个基底能否insert到线性空间中。
能就表示该数没有其他数与它线性相关,不能就表示可以由其他向量线性组合出来(就是可以跟n-r个数中的一个进行交换)。
下面是口胡,自己都不知道在说什么
进行枚举的时候枚举的是原来的数值而不是线性空间的基底数值。基底数值有点类似于正交分解后的基向量(比如这道题 2 ≤ 1 0 18 2\leq10^{18} 21018,转换成二进制一共有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);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值