可持久化Trie:
一般是可持久化 01Trie。01Trie 就是维护一个数字的二进制串的trie,每个结点可以维护数字个数。在求异或最大值(最小值)时,可以从高位到低位贪心判断每一位。
例如 :从 a 数组中找一个数使得它和 valvalval 异或最大,将a数组维护到一棵 01trie 中,从高位到低位枚举valvalval的每一位 p,同时在字典树上贪心地沿根节点一路走到叶子结点:若 son[rt][p⨁1]son[rt][p \bigoplus 1]son[rt][p⨁1] 存在(或者说num[son[rt][p⨁1]]>0num[son[rt][p \bigoplus 1]] > 0num[son[rt][p⨁1]]>0),则rt=son[rt][p⨁1]rt = son[rt][p \bigoplus 1]rt=son[rt][p⨁1],且答案 ans=ans⨁(1<<p)ans = ans \bigoplus (1 << p)ans=ans⨁(1<<p),否则 rt=son[rt][p]rt = son[rt][p]rt=son[rt][p]。
可持久化trie,就是在01trie的基础上,可以回退到前面的版本。其思想同于主席树(都是二叉树),当插入一个新的数字时,在trie上只会修改一条链,因此每次插入一个数字只要新建一条链即可,它的动态开点方式(trie本身就需要动态开点)和继承方式都和主席树相似。
题解:
将后缀和形式改成前缀和形式,求的是 xxx ^ b[n]b[n]b[n] ^ b[p]b[p]b[p](l−1≤p≤r−1l - 1 \leq p \leq r - 1l−1≤p≤r−1),b[n] 和 b[p] 代表前缀异或和。将 b 数组的前缀异或和维护到一棵可持久化 trie 上,询问时依据 sum[r][p]−sum[l−1][p]>0sum[r][p] - sum[l - 1][p] > 0sum[r][p]−sum[l−1][p]>0 是否成立来往下走得到答案。注意r 和 l 都需要减一,若 r - 1 = 0,前面没有建立trie,虚节点trie的查询得到的结果会是0,而不是前缀异或和,需要特判 r - 1 = 0的情况,或者将 0 插入到 trie中。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e5 + 10;
int n,m;
int root[maxn * 2],b[maxn * 2],sz; //trie部分
struct trie{ //可持久化字典树
int sz,son[maxn * 40][2],sum[maxn * 40];
void upd(int &rt,int v) {
//类似主席树的更新,每一步都要更新一个结点
++sz;
son[sz][0] = son[rt][0];
son[sz][1] = son[rt][1];
sum[sz] = sum[rt] + 1;
rt = sz;
int t = rt;
for(int i = 24; i >= 0; i--) {
int p = (v >> i) & 1;
++sz;
int s = son[t][p];
sum[sz] = sum[s] + 1;
son[sz][0] = son[s][0];
son[sz][1] = son[s][1];
son[t][p] = sz;
t = son[t][p];
}
}
int qry(int l,int r,int v) {
int ans = 0;
int L = root[l],R = root[r];
for(int i = 24; i >= 0; i--) {
int p = (v >> i) & 1;
if(sum[son[R][p ^ 1]] - sum[son[L][p ^ 1]] > 0) {
L = son[L][p ^ 1],R = son[R][p ^ 1];
ans |= (1 << i);
} else {
L = son[L][p],R = son[R][p];
}
}
return ans;
}
}trie;
char op[2];
int main() {
scanf("%d%d",&n,&m);
int val = 0;
for(int i = 1,x; i <= n; i++) {
scanf("%d",&x);
b[i] = b[i - 1] ^ x;
}
for(int i = 1; i <= n; i++) {
root[i] = root[i - 1];
trie.upd(root[i],b[i]);
}
for(int i = 1; i <= m; i++) {
scanf("%s",op);
int x,y,z;
if(op[0] == 'A') {
n++;
scanf("%d",&x);
b[n] = b[n - 1] ^ x;
root[n] = root[n - 1];
trie.upd(root[n],b[n]);
} else {
scanf("%d%d%d",&x,&y,&z);
x = max(0,x - 2);
y = max(0,y - 1);
if(y == 0) printf("%d\n",b[n] ^ z);
else printf("%d\n",trie.qry(x,y,b[n] ^ z));
}
}
return 0;
}