分析:
因为异或满足可减性,可以考虑像前缀和一样维护前缀异或和,即
令
s
n
=
a
1
⊕
a
2
⊕
⋯
⊕
a
n
s_n = a_1 \oplus a_2 \oplus \cdots \oplus a_n
sn=a1⊕a2⊕⋯⊕an
则有
a
l
⊕
a
l
+
1
⊕
⋯
⊕
a
r
=
s
r
⊕
s
l
−
1
a_l \oplus a_{l + 1} \oplus \cdots \oplus a_r = s_r \oplus s_{l - 1}
al⊕al+1⊕⋯⊕ar=sr⊕sl−1
显然的,有
s
0
=
0
s_0 = 0
s0=0(异或单位)
那么添加操作就非常好处理了,重点是询问操作。对于每一个询问l r x
,询问显然可以转化为找到一个
p
∈
[
l
−
1
,
r
−
1
]
p \in [l - 1,r - 1]
p∈[l−1,r−1],使
s
p
−
1
⊕
s
n
⊕
x
s_{p - 1} \oplus s_n \oplus x
sp−1⊕sn⊕x最大。
考虑维护一个可持久化Trie树,每次贪心的选择与 s n ⊕ x s_n \oplus x sn⊕x当前位相反的值即可,一开始要注意插入 s 0 s_0 s0
Code:
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn = 6e5 + 50;
int n,m,l,r,k,cnt,s[maxn],rt[maxn],a[20 * maxn],son[20 * maxn][2];
char c;
int read(){
int x = 0;
char c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + (c ^ 48),c = getchar();
return x;
}
void insert(int x,int last,int k,int now){
if(now < 0) return;
int t = k >> now & 1;
son[x][!t] = son[last][!t],son[x][t] = ++cnt,a[x] = a[last] + 1;
insert(son[x][t],son[last][t],k,now - 1);
}
int query(int l,int r,int k,int now){
if(now < 0) return 0;
int t = k >> now & 1;
if(son[r][!t] - son[l][!t] > 0) return (1 << now) + query(son[l][!t],son[r][!t],k,now - 1);
else return query(son[l][t],son[r][t],k,now - 1);
}
int main(){
n = read(),m = read(),rt[1] = ++cnt;
insert(rt[1],0,0,23);//插入s[0],为了防止下标出现负数,rt下标从一开始计算
for(int i = 1; i <= n; i ++){
s[i] = s[i - 1] ^ read(),rt[i + 1] = ++cnt;
insert(rt[i + 1],rt[i],s[i],23);
}
for(int i = 1; i <= m; i ++){
c = getchar();
if(c == 'A'){
s[n + 1] = s[n] ^ read(),n ++,rt[n + 1] = ++cnt;
insert(rt[n + 1],rt[n],s[n],23);
}else{
l = read(),r = read(),k = read();
printf("%d\n",query(rt[l - 1],rt[r],k ^ s[n],23));
}
}
return 0;
}