分析:
求第k大:把k二进制拆分,如果k的第i位上是1,ans^=b[i]
这是什么道理呢?
异或消元最后得到的是一组基
给出n个数能够异或出来的值,都是这些基线性组合形成的数
方便起见,我们把矩阵消成对角线型(比较好理解)
这样1只会出现在对角线上
cnt
c
n
t
:对角线上有多少个1
显然我们能得到
1<<cnt
1
<<
c
n
t
个不同的异或值(包括0)
然而存在一个问题:0是否真的可行
我们需要特殊处理一下0:
如果
cnt=n
c
n
t
=
n
,就说明每个数字都有会贡献1个1,那么就绝对不可能取到0
if (cnt==n) x++; //0不可以达到
在最后统计答案的时候,我们默认0可以达到
所以只有x>now的时候才统计
这样最后的x应该是等于1,这个1就是给0留的位置
for (int i=63;i>=0;--i) {
if(b[i]) {
ll now=(1LL<<(tmp-1));
if(x>now) x-=now,ans^=b[i];
tmp--;
}
}
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=10005;
int n,m,K;
ll a[N],b[100];
void cal() {
for (int i=1;i<=n;i++)
for (int j=63;j>=0;j--)
if (a[i]>>j&1) {
if (b[j]) a[i]^=b[j];
else {
b[j]=a[i];
for (int k=j-1;k>=0;k--)
if (b[k]&&(b[j]>>k&1)) b[j]^=b[k];
for (int k=j+1;k<=63;k++)
if (b[k]>>j&1) b[k]^=b[j];
break;
}
}
}
int main()
{
int T;
scanf("%d",&T);
for (int cas=1;cas<=T;cas++) {
memset(b,0,sizeof(b));
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
cal();
int cnt=0;
for (int i=0;i<=63;i++) if (b[i]) cnt++; //一共能组成(1<<cnt)个不同的数
scanf("%d",&m);
printf("Case #%d:\n",cas);
for (int i=1;i<=m;i++) {
ll x;
scanf("%lld",&x);
if (cnt==n) x++; //0不可以达到
if (x>(1LL<<cnt)) {printf("-1\n");continue;}
ll ans=0;
int tmp=cnt;
for (int i=63;i>=0;--i) {
if(b[i]) {
ll now=(1LL<<(tmp-1));
if(x>now) x-=now,ans^=b[i]; //x>now (0也要算上)
tmp--;
}
}
printf("%lld\n",ans);
}
}
return 0;
}