他要求一个区间内的位置到结尾处异或和的最大值,我们转换一下,设S[i]表示前i个位置上的数异或起来的结果,即异或值的前缀和。
于是题目的要求a[p] ^ a[p+1] ^… a[n] ^ x = s[p-1] ^ s[n] ^ x.。于是题目就相当于变成了,已知一个值val = s[n]^x,求一个位置p满足 l-1 =< p <= r-1,使得s[p] ^ val的这个值最大
这样的话我们只需要将trie可持久化后,每次区间询问位置合法(即两个版本间是否有合法位置)是查询val每一位上相反的位置的区间条件是否合法,用一个cnt数组来记录trie的尾结点对应的版本的信息,从而达到p的区间要求
最后,如果询问的区间左端点是1的话,可能会从0号去的最大的异或和,因此我们需要,在执行插入操作的时候,预先处理插入一个0号节点来解决这个问题。
具体操作实现看代码:
可以参考算法竞赛进阶指南P252页
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 6e5+7;
//可持久化Trie
int n,m;
int trie[MAXN*24][2],root[MAXN<<1],cnt[MAXN*24],size;
int s[MAXN<<1];//异或和
void insert(int now,int pre,int k,int x)
{
if(k < 0) return ;
int i = (x>>k)&1;//把第k位拿上来
trie[now][!i] = trie[pre][!i];//之前的都连在一起
trie[now][i] = ++size;//新插入的单独开节点
cnt[trie[now][i]] = cnt[trie[pre][i]]+1;//记录当前的版本
insert(trie[now][i],trie[pre][i],k-1,x);//递归插入
}
int query(int r,int l,int k,int x)
{
if(k < 0) return 0;
int i = (x>>k)&1;
if(cnt[trie[r][!i]] > cnt[trie[l][!i]])//代表这个区间可行
return (1<<k) + query(trie[r][!i],trie[l][!i],k-1,x);
else
return query(trie[r][i],trie[l][i],k-1,x);
}
int main()
{
scanf("%d%d",&n,&m);
//要把0先插入trie中
root[0] = ++size;
insert(root[0],0,24,0);
for(int i = 1;i <= n;i ++){
int x;
scanf("%d",&x);
s[i] = s[i-1]^x;
root[i] = ++size;
insert(root[i],root[i-1],24,s[i]);
}
while(m--){
char op;int l,r,x;
cin>>op;
switch(op){
case 'A':
scanf("%d",&x);
n++;//在序列尾添加元素的话 就是在尾部申请一个新的节点
s[n] = s[n-1]^x;
root[n] = ++size;
insert(root[n],root[n-1],24,s[n]);
break;
case 'Q':
scanf("%d%d%d",&l,&r,&x);
l--,r--;
//printf("-----\n");
int ans;
if(l == 0) ans = query(root[r],0,24,s[n]^x);
else ans = query(root[r],root[l-1],24,s[n]^x);
//printf("-----\n");
printf("%d\n",ans);
break;
}
}
return 0;
}