题目链接:B - Operation
题意:多组数据,首先给出一个序列,然后m次操作:1.查询区间[L,R]内子集的最大异或值,2.在序列末尾插入一个数字。
强制在线。
学到了一种将贪心与线性基结合的思想。
f[R][32] 表示将区间[1,R]内的数字加入线性基,pos[R][32]记录基内数字的位置。
查询区间[L,R]可以看做查询线性基f[R][32]中位置大于等于L的基是否对答案产生影响。
在向基中插入该元素时我们从高位到低位依次考虑,如果这一位上的基为空,那么直接加入,并记录位置;否则的话,如果该位上这个数字在序列中出现的位置比这个数字要靠前的话,就将基中这一位的数字替换掉,同时更新位置,接下来考虑将换出来的数字向基的低位尝试加入线性基中即可。
也就是说我们使得f[R][32]中对基产生贡献的元素尽量的靠近R。
插入与查询都是O(30)的复杂度。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5e5+7;
int f[maxn][32],pos[maxn][32];
void add(int id,int x){
int k=id;
for(int i=30;i>=0;--i)
f[id][i]=f[id-1][i],pos[id][i]=pos[id-1][i];
for(int i=30;i>=0;--i)
if(x&(1<<i)){
if(!f[id][i]){
f[id][i]=x;
pos[id][i]=k;
return ;
}
else{
if(k>pos[id][i]){
swap(x,f[id][i]);
swap(k,pos[id][i]);
}
x^=f[id][i];//不管换没换出数字都应该将x的高位的1消掉;
}
}
}
int myfind(int l,int r){
int res=0;
for(int i=30;i>=0;--i)
if(pos[r][i]>=l&&(res^f[r][i])>res) res^=f[r][i];
return res;
}
int main(){
int t;
scanf("%d",&t);
int n,m;
int x;
int id,l,r;
int res=0;
while(t--){
scanf("%d%d",&n,&m);
res=0;
for(int i=1;i<=n;++i){
scanf("%d",&x);
add(i,x);
}
while(m--){
scanf("%d",&id);
if(id){
scanf("%d",&x);
x^=res;
add(++n,x);
}
else{
scanf("%d%d",&l,&r);
l=(l^res)%n+1;
r=(r^res)%n+1;
if(l>r) swap(l,r);
res=myfind(l,r);
printf("%d\n",res);
}
}
for(int i=1;i<=n;++i)
for(int j=0;j<=30;++j) f[i][j]=pos[i][j]=0;
}
return 0;
}