注意
- long long
- 成功插入线性基之后记得返回
线性基用来干什么
维护一个序列的异或值:
记(d[])为序列(a[])的线性基,那么:
- (d[])中任意一些元素异或的集合与(a[])中任意一些元素异或的集合相同
- (d[])中任意一些元素异或不为0
- (d[])是所有满足条件1的集合中最小的
其中(d[i])的第(i)位上为1
插入
从高位往低位找
- 如果当前为没有值,直接插入并返回
- 否则,把(x)异或上(d[i]),继续找
int INS(LL x){
tep(i,60,0){
if(!((x>>i)&1))continue;
if(!d[i]){
d[i]=x;
cnt++;//记录线性基中有多少元素
return 1;
}
x^=d[i];
}
return 0;//返回值表示是否能够成功插入(如果原来的线性基中已经存在某一些
//数异或和为X,那么x就没有必要被插入。这个时候a[]异或可以得到0,需要特判
}
查询
最大值
从高位往低位扫,如果(ansoplus d[i]>ans),那么把(ans)异或上(d[i]),否则说明(ans)的第(i)为已经有(1),异或(d[i])只会使答案变小,就skip
最小值
就是(d[])里面最小的
第(k)小
就是求所有能异或得到的数中第(k)小的那个
Part1
先重构(d[]),使得对于每一个(i),最多只有一个(d[j])的第(i)位上为1
rep(i,1,maxn)rep(j,0,i-1){
if((d[i]>>j)&1)d[i]^=d[j];
}
正确性:
(d[i]oplus d[j])后,线性基里的元素从(d[i],d[j])变成了(d[i]oplus d[j],d[j]),可以发现,这两个元素异或得到的集合还是不变的,所以整个集合的异或值也不会变
Part2
先上代码
rep(i,0,60){
if(d[i]!=0){
if(k&1)ans^=d[i];
k/=2;
}
}
没想到吧,二进制真是奇妙,自己琢磨吧
Part3 Notice
注意判断0。如果原序列可以异或得到0,那么k要减1
猴啦上代码啦
Code
#include<bits/stdc++.h>
#define rep(X,A,B) for(int X=A;X<=B;X++)
#define tep(X,A,B) for(int X=A;X>=B;X--)
#define LL long long
const int N=1000010;
const int M=2000010;
const int maxn=60;
using namespace std;
void read(int &x){
scanf("%d",&x);
}
int n,Q,flg,cnt=0;
LL d[N],tot;
int INS(LL x){
tep(i,60,0){
if(!((x>>i)&1))continue;
if(!d[i]){
d[i]=x;
cnt++;
return 1;
}
x^=d[i];
}
return 0;
}
void READ(){
LL x;
flg=1;
cnt=0;
read(n);
rep(i,0,60)d[i]=0;
rep(i,1,n)scanf("%lld",&x),flg&=INS(x);
rep(i,1,maxn)rep(j,0,i-1){
if((d[i]>>j)&1)d[i]^=d[j];
}
tot=1;
rep(i,1,cnt)tot*=2;tot--;
read(Q);
}
LL SOLVE(){
LL k,ans=0;
scanf("%lld",&k);
if(!flg)k--;
if(k>tot)return -1;
rep(i,0,60){
if(d[i]!=0){
if(k&1)ans^=d[i];
k/=2;
}
}
return ans;
}
int main(){
int T;
read(T);
rep(op,1,T){
printf("Case #%d:
",op);
READ();
while(Q--)printf("%lld
",SOLVE());
}
return 0;
}