题目描述
给定一个非负整数序列 ({a}),初始长度为(n)。
有 (m) 个操作,有以下两种操作类型:
A x:添加操作,表示在序列末尾添加一个数 (x),序列的长度 (n+1)。
Q l r x:询问操作,你需要找到一个位置 (p),满足(x⊕a[p]⊕a[p+1]⊕...⊕a[N]⊕x)最大,输出最大是多少。
(N,M≤300000)
分析
可持久化trie树模板
设(s[i])表示前(i)个数(⊕)起来的结果,根据异或的性质:
(a_p⊕a_{p+1}⊕...⊕a[n]⊕x = s[p-1]⊕s[n]⊕x)
对于添加操作,直接插入即可
对于查询操作:
维护一个数组(b_i),表示节点(b_i)的子树中的最大版本号(最大下标)
显然,只有在最大版本号不小于(l-1)时该节点才合法,在合法的前提下尽可能地往与当前位相反的指针走
(code)
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 600010;
int n,m,child[MAXN*24][2], tot;
int s[MAXN],a[MAXN],root[MAXN],maxindex[MAXN*24];
void update(int node){
maxindex[node] = max(maxindex[child[node][0]],maxindex[child[node][1]]);
}
void insert(int node,int now,int ind,int last){
if(now<0){
maxindex[node] = ind;
return;
}
int c = s[ind]>>now&1;
if(last) child[node][c^1] = child[last][c^1];
child[node][c] = ++tot;
insert(child[node][c],now-1,ind,child[last][c]);
update(node);
}
int query(int node,int now,int val,int limit){
if(now<0){
return s[maxindex[node]]^val;
}
int c = val>>now&1;
if(maxindex[child[node][c^1]]>=limit){
return query(child[node][c^1],now-1,val,limit);
}
else{
return query(child[node][c],now-1,val,limit);
}
}
int main(){
cin>>n>>m;
maxindex[0] = -1;//坑
root[0] = ++tot;
insert(root[0],23,0,0);
for(int i=1;i<=n;i++){
int x;
scanf("%d",&x);
s[i] = s[i-1]^x;
root[i] = ++tot;
insert(root[i],23,i,root[i-1]);
}
for(int i=1;i<=m;i++){
char opt[2];
scanf("%s",opt);
if(opt[0]=='A'){
int val;
scanf("%d",&val);
root[++n] = ++tot;
s[n] = s[n-1]^val;
insert(root[n],23,n,root[n-1]);
}
else{
int l,r,x;
scanf("%d%d%d",&l,&r,&x);
printf("%d
",query(root[r-1],23,x^s[n],l-1));
}
}
}