题目
题意: 给你n个数字。m次操作+询问 操作:在序列末尾加一个数。询问:区间[l,r]中的数任选任意个数异或值最大。
思路: 贪心+线性基。我们假如数固定的话 肯定要转化为上三角形式,贪心依次从高位到地位选择。现在每次插入一个属就动态维护当前数目的线性基。用f[i][j]表示前i个数第j位上的线性基的数目是多少,pos[i][j]是前i个数第j位上的线性基在原序列出现的位置是哪里。
动态维护的时候也是从高位向地位贪心维护,尽量下标越靠右越替换原有的线性基f[i][j],变成重新插入一个数x^f[i][j]。询问时发f[r][j]中的线性基在满足pos[r][j]>=l条件贪心选择。
#include<cstdio>
#include<iostream>
using namespace std;
const int N=5e5+2;
int f[N<<1][32],pos[N<<1][32];
inline void add(int i,int x){//动态维护f[i][j],前i个数第j位线性基的数,pos[i][j]他在原序列的位置
int k=i;
for(int j=30;j>=0;--j) f[i][j]=f[i-1][j],pos[i][j]=pos[i-1][j];
for(int j=30;j>=0;--j){
if(x>>j){
if(!f[i][j]){//直接插入
f[i][j]=x,pos[i][j]=k;
break;
}
else{//假如小标更靠右的话 将原有的线性基b更换 变成了插入a a^b.
if(k>pos[i][j]) swap(x,f[i][j]),swap(k,pos[i][j]);
x^=f[i][j];
}
}
}
}
int main(){
int T;scanf("%d",&T);
while(T--){
int n,m,x,ans=0;scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) scanf("%d",&x),add(i,x);
while(m--){
int op;scanf("%d",&op);
if(op) scanf("%d",&x),add(++n,x^ans);
else{
int l,r;scanf("%d%d",&l,&r),l=(l^ans)%n+1,r=(r^ans)%n+1;
if(l>r) swap(l,r);
ans=0;
for(int j=30;j>=0;--j)//贪心从高位到低位选择。
if(pos[r][j]>=l&&(ans^f[r][j])>ans) ans^=f[r][j];
printf("%d\n",ans);
}
}
}
}