题目
Description
给定一个非负整数序列{a},初始长度为N。
有M个操作,有以下两种操作类型:
1、Ax:添加操作,表示在序列末尾添加一个数x,序列的长度N+1。
2、Qlrx:询问操作,你需要找到一个位置p,满足l<=p<=r,使得:
a[p] xor a[p+1] xor … xor a[N] xor x 最大,输出最大是多少。
Input
第一行包含两个整数 N ,M,含义如问题描述所示。
第二行包含 N个非负整数,表示初始的序列 A 。
接下来 M行,每行描述一个操作,格式如题面所述。
Output
假设询问操作有 T个,则输出应该有 T行,每行一个整数表示询问的答案。
Sample Input
5 5
2 6 4 3 6
A 1
Q 3 5 4
A 4
Q 5 7 0
Q 3 6 6
Sample Output
4
5
6
HINT
对于测试点 1-2,N,M<=5 。 对于测试点 3-7,N,M<=80000 。 对于测试点 8-10,N,M<=300000 。 其中测试点 1, 3, 5, 7, 9保证没有修改操作。 0<=a[i]<=10^7。
题解
把操作离线下来,相当于一个一个在后面删数,往可持久化$Trie$中插后缀异或和
记录序列现在的长度$N$,每次查询就是询问和$x\oplus s_{N+1}$异或的最大值
代码
# include<iostream>
# include<cstring>
# include<cstdio>
# include<algorithm>
using namespace std;
const int MAX=6e5+5,N=2e7+5;
struct p{
int x,l,r;
bool fl;
}qu[MAX];
int N_,n,m,tot;
struct Trie{
int siz[N];
int son[N][2];
void ins(int &k,int pre,int x,int o=24)
{
k=++tot,son[k][0]=son[pre][0],son[k][1]=son[pre][1],siz[k]=siz[pre]+1;
if(o==-1) return;
if(x&(1<<o)) ins(son[k][1],son[pre][1],x,o-1);
else ins(son[k][0],son[pre][0],x,o-1);
}
int ask(int L,int R,int x,int o=24)
{
if(o==-1) return 0;
int v=bool(x&(1<<o))^1,tt=siz[son[R][v]]-siz[son[L][v]];
if(tt) return ask(son[L][v],son[R][v],x,o-1)+(1<<o);
return ask(son[L][v^1],son[R][v^1],x,o-1);
}
}Tree;
int xsum[MAX],a[MAX],rt[MAX],ans[MAX];
char A[5];
int read()
{
int x(0);
char ch=getchar();
for(;!isdigit(ch);ch=getchar());
for(;isdigit(ch);x=x*10+ch-48,ch=getchar());
return x;
}
int main()
{
N_=n=read(),m=read();
for(int i=1;i<=n;++i)
a[i]=read();
for(int i=1;i<=m;++i)
{
scanf("%s",A);
if(A[0]=='A') a[++N_]=qu[i].x=read();
else if(A[0]=='Q') qu[i].fl=1,qu[i].l=read(),qu[i].r=read(),qu[i].x=read();
}
for(int i=N_;i>=1;--i)
xsum[i]=xsum[i+1]^a[i];
for(int i=1;i<=N_;++i)
Tree.ins(rt[i],rt[i-1],xsum[i]);
for(int i=m,_N=N_+1;i>=1;--i)
if(!qu[i].fl) --_N;
else if(qu[i].fl) ans[i]=Tree.ask(rt[qu[i].l-1],rt[qu[i].r],qu[i].x^xsum[_N]);
for(int i=1;i<=m;++i)
if(qu[i].fl) printf("%d\n",ans[i]);
return 0;
}