题意:
有
n
n
n个数和
q
q
q个询问,每个询问给出一个正整数
k
k
k,问这
n
n
n个数异或组合出的所有数中第
k
k
k小的是什么数,若不存在则输出
−
1
-1
−1。
思路:
首先构建出这
n
n
n个数的异或线性基。
假设非零线性基共有 t o t tot tot个,从小到大排列后,对于第 x x x个线性基,前 1 − ( x − 1 ) 1-(x-1) 1−(x−1)个线性基无论怎样线性组合,其结果一定小于第 x x x个线性基。
因为第 x x x个线性基假设最高位的 1 1 1在第 k k k位,因为异或不存在进位,故比它小的基无论怎么组合都组合不出二进制位大于等于 k k k的一个 1 1 1。
这样的话,线性基的线性组合大小排序很类似于二进制的大小排序,比如:
10000
10000
10000 一定比
01111
,
01101
,
01110
01111,01101,01110
01111,01101,01110等等形如
0
x
x
x
x
0xxxx
0xxxx的二进制数大。
故可考虑对 k k k二进制分解,若其第 i i i位为1,则加上从小到大排序后第 i i i小的线性基的贡献。
故对于
t
o
t
tot
tot个线性基,线性组合的情况一共有
2
t
o
t
−
1
2^{tot} - 1
2tot−1种。
但这样并没有包括
0
0
0的情况。
所以可以特判一下,若 n n n个向量全部都用来构成线性基,则不会存在异或后为 0 0 0的情况。
故此题得解。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int A = 1e4 + 10;
ll a[A],b[110],c[A];
int n,zero,tot;
void init(){
zero = tot = 0;
memset(b,0,sizeof(b));
for(int i=1 ;i<=n ;i++) for(int j=62 ;j>=0 ;j--){
if((a[i]>>j)&1){
if(b[j]) a[i] ^= b[j];
else{
b[j] = a[i];tot++;
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<=62;k++) if(((b[k]>>j)&1)) b[k] ^= b[j];
break;
}
}
}
zero = (tot<n);tot = 0;
for(int i=0 ;i<=62 ;i++) if(b[i]) c[tot++] = b[i];
}
ll solve(ll k){
if(zero) k--;
if(k >= (1LL<<tot)) return -1;
ll ans = 0;
for(int i=0 ;i<=62 ;i++) if((k>>i)&1) ans ^= c[i];
return ans;
}
int main(){
int T,_=1;scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=1 ;i<=n ;i++) scanf("%I64d",&a[i]);
init();
int q;scanf("%d",&q);
printf("Case #%d:\n",_++);
while(q--){
ll k;scanf("%I64d",&k);
printf("%I64d\n",solve(k));
}
}
return 0;
}