题意:大概就是给你一个数组,然后问你这个数组的异或集合中的第k小是几,异或集合指从这个数组中选取任意的非空子集异或得到的数字的集合。
分析:线性基裸题。先求出线性基,对线性基进行高斯消元。然后对K 进行二进制分解,如果第i位为1,就将第i小的向量异或起来。这样总能得到第K小的线性基,因为高斯消元后的线性基是一个对角矩阵,它们没有重叠的部分,异或谁就相当于加上谁,这和二进制的位权刚好想对应,大的向量对应高位权,小的向量对应低位权。
对于有 x 个向量的线性基,可以异或出 2 x 2^x 2x - 1个不同的向量,如果线性基的大小小于原数组的大小,那么还可以异或出0,因为不在线性基内的数字可以被线性基唯一表示,再互相异或就得到0。所以这里分情况讨论,如果能异或出0,我们将K–(因为多了个0,线性基是线性无关的向量组是不能异或出0的,),否则K不变
如果询问的K 大于等于 2 x 2^x 2x,说明超出了异或集合的范围,也就是不存在第K小,输出-1,否则按二进制位权异或。
代码:
#include<bits/stdc++.h>
using namespace std;
int t,n,q,cnt;
long long x;
const int maxn = 1e5 + 10;
const int mx = 62;
long long a[maxn];
long long b[65];
long long tmp[65];
long long top = 0;
int main() {
scanf("%d",&t);
while(t--) {
scanf("%d",&n);
top = 0;
memset(b,0,sizeof b);
for(int i = 1; i <= n; i++)
scanf("%lld",&a[i]);
for(int i = 1; i <= n; i++) { //线性基
for(int j = mx; j >= 0; j--) {
if(a[i] >> j & 1) {
if(b[j]) a[i] = a[i] ^ b[j];
else {
b[j] = a[i];
for(int k = j - 1; k >= 0; k--)
if((b[j] >> k & 1) && b[k])
b[j] ^= b[k];
for(int k = j + 1; k <= mx; k++)
if(b[k] >> j & 1 && b[k])
b[k] ^= b[j];
break;
}
}
}
}
for(int j = 0; j <= mx; j++) {
if(b[j]) tmp[top++] = b[j];
}
printf("Case #%d:\n",++cnt);
scanf("%d",&q);
for(int i = 1; i <= q; i++) {
scanf("%lld",&x);
long long ans = 0;
if(top < n) x--;
if(x >= (1ll << top)) puts("-1");
else {
for(int j = 0; j < top; j++)
if(x >> j & 1) ans ^= tmp[j];
printf("%lld\n",ans);
}
}
}
return 0;
}