原题: http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1002&cid=848
题意:
给出n个数的数组,有两个操作
- 在 [ l , r ] [l,r] [l,r]中选择一些数,使之异或最大,输出最大值;
- 在数组后面塞进去一个数。
解析:
选择一些数异或起来最大,那么就是线性基无疑了。不会线性基的同学可以看一下我的博客。
我们考虑维护每一个位置往左的31个基的位置。假设我已经知道了 i − 1 i-1 i−1往左的31个基的位置和值。那么维护 i i i时,从高到低维护31个基。
- 如果这个基之前没有出现过,则直接选择当前数作为这个位的基。
- 如果出现过,则去比较位置。因为我们定下 R R R去查询时,基越靠右,越可能落在我的区间内。所以将靠右的作为基,将左边的值异或后,再往下更新。
代码:
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define rrep(i,a,b) for(int i=a;i>=b;i--)
const int maxn=1e6+5;
int ji[maxn][31];
int pos[maxn][31];
void deal(int n,int val){
int nex=n;
rep(i,0,30)ji[n][i]=ji[n-1][i],pos[n][i]=pos[n-1][i];
rrep(i,30,0){
if(val&(1<<i)){
if(!ji[n][i]){
ji[n][i]=val;pos[n][i]=nex;return;
}
if(pos[n][i]<nex){
swap(val,ji[n][i]);
swap(nex,pos[n][i]);
}
val^=ji[n][i];
}
}
}
int main(){
int t;scanf("%d",&t);
while(t--){
int lst=0;
int n,m;scanf("%d%d",&n,&m);
rep(i,1,n){
int val;scanf("%d",&val);
deal(i,val);
}
while(m--){
int f;scanf("%d",&f);
if(f==1){
int val;scanf("%d",&val);
val^=lst;
deal(++n,val);
}
else{
int l,r;scanf("%d%d",&l,&r);
l^=lst,r^=lst;
l%=n,r%=n;
l++,r++;
if(l>r)swap(l,r);
int ans=0;
rrep(i,30,0){
if(pos[r][i]>=l){
if((ans^ji[r][i])>ans){
ans^=ji[r][i];
}
}
}
printf("%d\n",ans);
lst=ans;
}
}
}
}