这个题,用的可持久化字典树来维护区间异或和
感觉用法跟主席树差不多,会主席树,可持久化字典树应该不难学
记 res[i] 为 [1 , i ] 的 a[] (程序中我使用的是ans[]) 的异或和
则求max(res[p]res[n]x) (l-1<=p<=r-1)
每次加点将trie树的这条链的权都+1
修改当然是新建一个结点
然后查询的时候判断一个结点存在,只要做区间减法判权是否非0
即若 trie[r].size - trie[l-1].size =0则该结点不存在
查询的贪心策略,其实很简单,res[n]^x的二进制某个位置 ind 为1,我们就找可持久化字典树区间对应位置是否存在0,存在,则这个位置的异或值为1 << ind,不存在0就只能选择1(因为这个区间肯定是有数的),所以这个位置的异或值为0,如果位置ind是0的话,相反即可。
实现的时候数列开始加入一个数0会比较好处理
我的代码:
#include<cstdio>
using namespace std;
const int maxn=600000+100;
struct Trie{
int l,r;
int size;
}trie[maxn*24];
int tot,root[maxn];
int ans[maxn],res[maxn];
inline void Update(int &New,int Old,int v){
New=++tot;
trie[New]=trie[Old];
trie[New].size=trie[Old].size+1;
int now=New,pre=Old;
for(int i=24;i>=0;i--){
int tmp=(v>>i)&1;
int nodel=trie[pre].l,noder=trie[pre].r;
if(tmp){
trie[now].l=nodel;
trie[now].r=++tot;
trie[tot]=trie[noder];
trie[tot].size=trie[noder].size+1;
now=tot,pre=noder;
}
else{
trie[now].r=noder;
trie[now].l=++tot;
trie[tot]=trie[nodel];
trie[tot].size=trie[nodel].size+1;
now=tot,pre=nodel;
}
}
}
inline int Query(int l,int r,int v){
int query=0;
if(l<0) l=0;
for(int i=24;i>=0;i--){
int Lnodel=trie[l].l,Lnoder=trie[l].r;
int Rnodel=trie[r].l,Rnoder=trie[r].r;
int tmp=(v>>i)&1;
if(tmp){
if(trie[Rnodel].size-trie[Lnodel].size) query+=(1<<i),l=Lnodel,r=Rnodel;
else l=Lnoder,r=Rnoder;
}
else{
if(trie[Rnoder].size-trie[Lnoder].size) query+=(1<<i),l=Lnoder,r=Rnoder;
else l=Lnodel,r=Rnodel;
}
}
return query;
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
Update(root[0],root[0],0);
for(int i=1;i<=n;i++){
scanf("%d",&ans[i]);
res[i]=res[i-1]^ans[i];
Update(root[i],root[i-1],res[i]);
}
while(m--){
char ch=getchar();
while(ch<'A' || ch>'Z') ch=getchar();
if(ch=='A'){
n++;
scanf("%d",&ans[n]);
res[n]=res[n-1]^ans[n];
Update(root[n],root[n-1],res[n]);
}
else{
int l,r,x;
scanf("%d%d%d",&l,&r,&x);
printf("%d\n",Query(root[l-2],root[r-1],res[n]^x));
}
}
}