我们维护一个前缀异或和: s [ i ] = a [ 1 ] x o r a [ 2 ] x o r … a [ i − 1 ] x o r a [ i ] s[i] = a[1] \ xor\ a[2]\ xor\ … a[i-1] \ xor\ a[i] s[i]=a[1] xor a[2] xor …a[i−1] xor a[i]
则 a [ p ] x o r a [ p + 1 ] x o r … x o r a [ N ] x o r x a[p]\ xor\ a[p+1]\ xor\ …\ xor\ a[N]\ xor\ x a[p] xor a[p+1] xor … xor a[N] xor x 就相当于 s [ N ] x o r x x o r s [ p − 1 ] s[N] \ xor\ x\ xor\ s[p-1] s[N] xor x xor s[p−1]。
这样 S [ N ] x o r x S[N] \ xor\ x S[N] xor x就是一个定值 v a l val val,则相当于求一个 p p p 满足 l − 1 < = p − 1 < = r − 1 l-1 <= p-1 <= r-1 l−1<=p−1<=r−1 使得 v a l x o r s [ p ] val \ xor\ s[p] val xor s[p] 值最大。
因为是异或运算,我们可以利用 “最大异或对” 这道题的一些性质,使用 t r i e trie trie 树。我们维护前缀异或和,找到一个 p p p ,贪心地使得 s [ p ] s[p] s[p]的二进制最高位开始到最低位的每一个数字都尽量与 v a l val val 的二进制对应位相反(相同为0不同为1)
前缀异或和的每个版本都可以用持久化的 t r i e trie trie 记录下来。
对持久化 t r i e trie trie 的每个节点额外维护一个信息 m a x _ i d max\_id max_id ,表示其所属的最大的持久化版本,显然一个节点的 m a x _ i d max\_id max_id 等于其子节点中最大的版本号(因为子节点要么是连向之前的版本,要么创建了该节点后再创建子节点)。
因为要满足边界性所以要用可持久化trie树的保存历史版本来界定边界
如果一个节点的 m a x _ i d max\_id max_id 小于 l − 1 l-1 l−1 ,说明这个节点是 s [ l − 1 ] s[l-1] s[l−1] 插入之前就已经创建出来的节点,不应该考虑在内。
对持久化树的某一个版本的根节点开始往下访问,所能访问到的节点的版本不会超过该根节点的版本,所以该题只需要从 r − 1 r-1 r−1 版本开始访问即可满足取到的 p p p 小于等于 r − 1 r-1 r−1。
//trie树维护的是前缀异或和的二进制01串
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int N = 500007, M = N * 25;
/*
templateinline T read(T &x)
{
x=0;ll f=1;char c;
while(!isdigit(c=getchar()))if(c=='-')f=-1;
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return x*f;
}
*/
int n, m;
int sum[N];
int tr[M][2], max_id[M];
int root[N], idx;
//当前的区间位置(到时候[L,R]对比的就是这里的i)k表示当前是第几位,一共23位2^23,越高越大
void insert(int i, int k, int p, int q){//p上一个版本q下一个版本
if(k < 0){
max_id[q] = i;
return ;
}
int v = sum[i] >> k & 1;
if(p)//如果上个版本的当前结点是有东西的就继承一下
tr[q][v ^ 1] = tr[p][v ^ 1];
tr[q][v] = ++ idx;
insert(i, k - 1, tr[p][v], tr[q][v]);
max_id[q] = max(max_id[tr[q][0]], max_id[tr[q][1]]);
}
int query(int root, int C, int L){
int p = root;
for(int i = 23;i >= 0;i -- ){
int v = C >> i & 1;
//如果版本小于L-1,说明这个节点是s[l-1]插入之前就已经创建出来的节点,不应该考虑在内
if(max_id[tr[p][v ^ 1]] >= L)//如果与当前这一位相反的数存在并且该数的位置在范围以内(求异或相反为1更大)
p = tr[p][v ^ 1];
else p = tr[p][v];
}
return C ^ sum[max_id[p]];
}
int main(){
scanf("%d%d", &n, &m);
max_id[0] = -1;//因为id从0开始这里要取一个更小的
root[0] = ++ idx;
insert(0, 23, 0, root[0]);
for(int i = 1;i <= n; ++ i){
int x;
scanf("%d", &x);
sum[i] = sum[i - 1] ^ x;//前缀异或和
root[i] = ++ idx;
insert(i, 23, root[i - 1], root[i]);//可持久化都是跟上一个版本比较
}
char op[2];
int l, r, x;
while(m -- ){
scanf("%s", op);
if(*op == 'A'){
scanf("%d", &x);
n ++ ;
sum[n] = sum[n - 1] ^ x;
root[n] = ++ idx;
insert(n, 23, root[n - 1], root[n]);
}
else {
scanf("%d%d%d", &l, &r, &x);
//l<=p<=r,所以要查询r-1版本的
printf("%d\n", query(root[r - 1], sum[n] ^ x, l - 1));
}
}
}