给出一个序列长度
n
≤
3
e
5
n\leq3e5
n≤3e5的数组,支持两个操作,1.在数组的末尾插入一个数。2.给定区间
[
l
,
r
]
[l,r]
[l,r]和一个数
x
x
x,查询这个区间里面使得
(
⊕
i
=
p
n
a
i
)
⊕
x
(\oplus_{i=p}^{n}{a_{i}})\oplus x
(⊕i=pnai)⊕x最大的那个
p
p
p。
事实上
⊕
i
=
p
n
a
i
=
(
⊕
i
=
1
p
−
1
a
i
)
⊕
(
⊕
i
=
1
n
a
i
)
\oplus_{i=p}^{n}a_{i}=(\oplus_{i=1}^{p-1}a_{i})\oplus(\oplus_{i=1}^{n}a_{i})
⊕i=pnai=(⊕i=1p−1ai)⊕(⊕i=1nai),而后者很容易在修改的时候维护。问题转化为了求区间
[
l
−
1
,
r
−
1
]
[l-1,r-1]
[l−1,r−1]的最大前缀异或和了。
只考虑前缀这是一个普通01字典树的问题。现在对于区间要用到可持久化01字典树,对每个前缀
i
i
i都维护一个新的字典树,对于没有走到的分支直接用历史版本。另外记录一下每个节点的个数。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const ll INF=LONG_LONG_MAX;
const int N=6e5+7;
int n,m;
int a[N],sum[N];
int rt[N],ch[N*31][2],cnt[N*31];
int tot=0;
void insert(int i,int x) {
int p=rt[i-1];
int now=(rt[i]=++tot);
for(int i=30;i>=0;i--) {
int nxt=(x>>i)&1;
ch[now][nxt]=++tot;
ch[now][nxt^1]=ch[p][nxt^1];
now=ch[now][nxt];
p=ch[p][nxt];
cnt[now]=cnt[p]+1;
}
}
int query(int l,int r,int x) {
r=rt[r],l=rt[l];
int ans=0;
for(int i=30;i>=0;i--) {
int nxt=(x>>i)&1;
if(cnt[ch[r][nxt^1]]-cnt[ch[l][nxt^1]]>0) {
ans+=(1<<i);
l=ch[l][nxt^1];
r=ch[r][nxt^1];
}
else {
l=ch[l][nxt];
r=ch[r][nxt];
}
}
return ans;
}
int main() {
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
sum[i]=sum[i-1]^a[i];
insert(0,0);
for(int i=1;i<=n;i++)
insert(i,sum[i]);
while(m--) {
char opt[3];
int l,r,x;
scanf("%s",opt);
if(opt[0]=='A') {
n++;
scanf("%d",&a[n]);
sum[n]=sum[n-1]^a[n];
insert(n,sum[n]);
}
else if(opt[0]=='Q') {
scanf("%d%d%d",&l,&r,&x);
x^=sum[n];
l--;r--;
printf("%d\n",query(l-1,r,x));
}
}
return 0;
}