线性基
这东西本质上就是高斯消元
qaq
问题
我们有一个长度为n的序列,问序列中任选若干个能得到的异或最大值
我们考虑把每个数变为二进制,然后将其放入矩阵中
例如
3,4,5,10,15
0 0 1 1
0 1 0 0
0 1 0 1
1 0 1 0
1 1 1 1
然后对其异或高斯消元
也就是变成一组线性无关的基底
并且主元所在列只有主元这一个1[高斯消元一边就会变成这个样子]
然后你想求异或最大值就从最上面往下拿就行了
如果这一位还不是1,就异或上答案
否则就不拿
由于高斯消元复杂度不优
我们考虑更优秀的做法
//p[i]表示第i位为主元(1)的线性基
void ins(int x){
for(int i=62;i>=0;i--){
if((x&(1ll<<i))==0)continue;
if(!p[i]){//若这一位的线性基不存在,直接等于它
p[i]=x;
return ;
}
x^=p[i];//把这一位清0,看往下走能否有用
}
}
这样也能构造一组基
但想达到高斯消元的效果(p[i]有值,所有j>i的线性基,第i位都不为1)
我们需要下面的再操作
for(int i=62;i>=0;i--)
for(int j=i-1;j>=0;j--)
if(p[i]&(1<<j))
p[i]^=p[j];
而得到了这样的有什么用呢qaq
考虑我们想知道序列异或的第k小值
我们考虑把k二进制分解
由于我们刚刚得到的线性基的优秀性质
只有p[i]这个线性基第i位为1
那么对于k的二进制分解
可以证明我们对于每个i且k的第i位为1
我们异或上从大到小的第i-1个有值线性基就好了
#include<bits/stdc++.h>
#define int long long
using namespace std;
int tot=0;
int p[64],pos[64];
int read(){
int x=0,y=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')y=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*y;
}
void ins(int x){
for(int i=62;i>=0;i--){
if((x&(1ll<<i))==0)continue;
if(!p[i]){
p[i]=x;
return ;
}
x^=p[i];
}
}
void pre(){
for(int i=62;i>=0;i--)
for(int j=i-1;j>=0;j--)
if(p[i]&(1<<j))
p[i]^=p[j];
for(int i=62;i>=0;i--)
if(p[i])pos[tot++]=p[i];
tot--;
return ;
}
int query(int x){
if(!x)return 0;
int ans=0;
for(int i=62;i>=0;i--)
if(x&(1ll<<i)){
if(tot<i)return -1;
ans^=pos[tot-i];
}
return ans;
}
signed main(){
int t,cas=0;
t=read();
while(t--){
int n,q;
n=read();
tot=0;
memset(p,0,sizeof(p));
memset(pos,0,sizeof(pos));
for(int i=1;i<=n;i++)ins(read());
pre();
q=read();
int val=(n>tot+1);
printf("Case #%d:\n",++cas);
for(int i=1;i<=q;i++){
int k;
k=read();
printf("%lld\n",query(k-val));
}
}
return 0;
}