『高斯消元』异或运算(异或线性空间)

题目描述

给定你由N个整数构成的整数序列,你可以从中选取一些(甚至一个)进行异或(XOR)运算,从而得到很多不同的结果。

请问,所有能得到的不同的结果中第k小的结果是多少。

题解

显然在组成的若干个数异或起来后一定存在重复的数;我们需要做的就是去掉那些重复的数。显然在线性空间中如果一个数能被其他数字异或起来得到,高斯消元的结果一定为 0 0 0;所以我们去掉这些数即可。

在此基础上,我们就可以找到若干个数使得这些数字互相不能通过某一种方式进行异或运算来得到。

现在的问题就是统计第k小:例如有 3 3 3个数分别是 a 1 a_1 a1, a 2 a_2 a2 a 3 a_3 a3.且 a 1 > a 2 > a 3 . a_1>a_2>a_3. a1>a2>a3.显然画成矩阵的话,除了最后一列以外这三个数的矩阵都只有一个斜对角的 1. 1. 1.

  • 第一小就是 a 1 . a_1. a1.
  • 第二小就是 a 2 a_2 a2
  • 第三小就是 a 1   x o r   a 2 a_1\ xor\ a_2 a1 xor a2
  • 第四小就是 a 3 a_3 a3
  • 第五小就是 a 1 a_1 a1 x o r xor xor a 3 a_3 a3

那么显然只要对这一个 k k k进行二进制分解一下即可。细节的话再慢慢想吧。

还有一种特殊的情况,就是如果有数字被排除掉的话,还包含结果为 0 0 0的情况。细节理一下即可。

代码如下:

#include 

using namespace std;

const int N = 20000;

int n;
int cnt = 1;
long long a[N];

inline int read(void)
{
	int s = 0;char c = getchar();
	while (c<'0' || c>'9') c = getchar();
	while (c>='0' && c<='9') s = s*10+c-48, c = getchar();
	return s;
}

void print(void)
{
	for (int i=1;i<=n;++i) 
	{ 
		int t[100] = {};
		int k = a[i], s= 0 ;
		while (k) t[++s] = k&1, k >>= 1;
		for (int i=4;i;--i) cout<<t[i];
		cout<<endl;
	} 
	return;
}

void work(void)
{
	bool flag = 0;
	n = read();
	for (int i=1;i<=n;++i) scanf("%lld",a+i);
	for (int i=1;i<=n;++i)
	{
		for (int j=i;j<=n;++j) 
		    if (a[j]>a[i]) swap(a[i],a[j]);
	    if (a[i] == 0) { flag = 1; n = i-1; break; }
	    for (int j=63;j>=0;--j)//63 not 64
	    {
	        if ((a[i] >> j) & 1)
	        {
	        	for (int k=1;k<=n;++k) 
	        	    if (i ^ k && ((a[k] >> j) & 1)) a[k] ^= a[i];
	        	break;
	        }
	    }
	}
	int m = read();
	printf("Case #%d:\n",cnt ++);
	for (int i=1;i<=m;++i)
	{
		long long ans = 0,k;
		scanf("%lld",&k);
		if (flag == 1) k --;
		if (k >= pow(2,n)) 
		{
			puts("-1");
			continue;
		}
		for (int j=64;j>=1;--j) //j表示第几位 
		    if (k >> j-1 & 1) ans ^= a[n-j+1];
		printf("%lld\n",ans); 
	}
	return;
}

int main(void)
{
	int T = read();
	while (T --) work();
	return 0;
}

你可能感兴趣的:(数学·数学推导,[算法进阶指南]习题题解)