题意: T组测试,每次测试给n个long long 型数字,做Q次询问第k小的子集异或和值。
线性基的异或和是无法进位,A[i] > all{ 0……i-1的所有线性基的异或组合},
实际相当于二进制,求第k最小,实际求k的二进制表达中为1的项位置.
(第i位不为0即对应于线性基中第i位不为0的基在要选的组合中。必须注意的是: 线性基无法凑成子集异或和为0的情况。所以我们在建线性基组的时候,一定要判定是否在添加过程中,没有参与A中线性基的构建,直接被现有的基消为0,就表示有为0的情况。
(我们的二进制遍历是专门设计解决第k小异或和不为0的子集异或和,所以有0的情况,就是找第k-1小异或和)如果二进制完后,k不为0,即表示询问范围超出了结果的个数,输出-1。
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <vector>
#include <algorithm>
#define llt long long
using namespace std;
const int Size=5*1e4+10;
const int L=60;
int have_0;
llt A[65];
void add_BaseLine(llt x){
int i;
for(i=L;i>=0;--i)
if(x&(1ll<<i)){
if(A[i]) x^=A[i];
else {
for(int j=i-1;j>=0;--j)
if(x&(1ll<<j)) x^=A[j];
for(int j=i+1;j<=L;++j)
if(A[j]&(1ll<<i)) A[j]^=x;
A[i]=x;
return ;
}
}
have_0=1;//x能够被现有的基线性表示;
}
llt solve(llt k){
if(have_0) k--;
llt ans=0;
//二进制遍历组合
for(int i=0,j=0;i<=L;++i){
if(A[i]==0) continue;
if(k&(1ll<<j)) {
ans^=A[i]; k^=(1ll<<j);
}
++j;
}
if(k) return -1;//输入k超出结果范围
return ans;
}
int main(){
int T,kase=0;
scanf("%d",&T);
while(T--){
have_0=0;
int N;scanf("%d",&N);
memset(A,0,sizeof(A));
while(N--){
llt a;scanf("%lld",&a);
add_BaseLine(a);
}
int Q;
scanf("%d",&Q);
printf("Case #%d:\n",++kase);
while(Q--){
llt k;
scanf("%lld",&k);
printf("%lld\n",solve(k));
}
}
return 0;
}