版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_36056315/article/details/81153609
链接
https://www.lydsy.com/JudgeOnline/problem.php?id=3261
做法
我们先考虑没有修改操作,只有询问的话怎么做,我们记sum[i]表示i的后缀异或和,那么我们每个询问相当于查询区间对一个数x异或后的最大值,那么贪心很明显,位数从高到低,如果有数这个位置上可以和x异或起来是1,那么我肯定不会去选和x异或起来这位上是0的。根据这个性质,我们考虑trie树,如果当前节点的子节点有能和x异或起来为1的节点,就走下去,不然只能走另一边了,那么区间的话我们就可持久化一下,这个具体就和主席树是一模一样的了,这里不多赘述了。
接下来我们要考虑修改的问题,我们另sum[i]表示i的前缀异或和,注意这里和上面不同了,变成了前缀,那么我们每个点到n的异或和就可以表示为sum[i-1]^sum[n]了对吧,接下来的步骤就和上面是一样的了。
代码
#include<cstdio>
#include<algorithm>
#include<cctype>
#include<cstring>
#include<iostream>
#include<cmath>
#define LL long long
#define N (600005)
using namespace std;
int n,m,x,l,r,now,ans,cnt;
int sum[N],root[N],size[20000005],son[20000005][2];
char c[10];
template <typename T> void read(T&t) {
t=0;
bool fl=true;
char p=getchar();
while (!isdigit(p)) {
if (p=='-') fl=false;
p=getchar();
}
do {
(t*=10)+=p-48;p=getchar();
}while (isdigit(p));
if (!fl) t=-t;
}
void insert(int &u,int pre,int data,int ws){
u=++cnt;
size[u]=size[pre]+1;
//printf("%d %d %d\n",data,ws,size[u]);
if (ws==1) return;
son[u][0]=son[pre][0];
son[u][1]=son[pre][1];
if (data&(1<<ws-2)) insert(son[u][1],son[pre][1],data,ws-1);
else insert(son[u][0],son[pre][0],data,ws-1);
}
void query(int l,int r,int ws){
//printf("%d %d\n",ws,ans);
if (ws==1) return;
int cc=(now&(1<<ws-2));
if (cc) cc=0;
else cc=1;
//printf("ws %d %d\n",ws,cc);
if (size[son[r][cc]]-size[son[l][cc]]) ans+=(1<<(ws-2)),query(son[l][cc],son[r][cc],ws-1);
else query(son[l][cc^1],son[r][cc^1],ws-1);
}
int main(){
read(n),read(m);
insert(root[0],0,0,31);
for (int i=1;i<=n;i++){
read(x);
sum[i]=sum[i-1]^x;
insert(root[i],root[i-1],sum[i],31);
}
while (m--){
scanf("%s",c);
if (c[0]=='A'){
read(x);
n++;
sum[n]=sum[n-1]^x;
insert(root[n],root[n-1],sum[n],31);
}
if (c[0]=='Q'){
read(l),read(r),read(x);
now=sum[n]^x;
//printf("now %d\n",now);
ans=0;
int tt;
if (l==1) tt=0;
else tt=root[l-2];
query(tt,root[r-1],31);
printf("%d\n",ans);
}
}
return 0;
}