题意:
给出一个长度为N的正整数数组A,再给出Q个查询,每个查询包括3个数,L, R, X (L <= R)。求A[L] 至 A[R] 这R - L + 1个数中,与X 进行异或运算(Xor),得到的最大值是多少?
思路:
初学可持久化字典树,对它的理解还不是很深刻。主席树是可持久化的线段树,两者有相似之处。主席树中有版本这一概念,每一个版本都是新建一棵树,先复制上一个版本的内容,再做一些当前版本特有的修改,当然复制并不是直接复制,只是用一个指针指一下即可。那么同样,对于上面A数组的下标可以用版本表示,对于每个新的版本也要‘复制‘前一个版本的信息,并插入本版本的字符(这里就是0或1了),每插入一个字符都需要记录一下当前版本下的该节点的该字符的数量,就是上一个版本的对应节点的该字符数量加1(可看成前缀和)。这个字符的数量很重要,判断两个版本之间深度为x存不存在某一字符(记为c)需要通过比较两个版本深度为x的节点的c的数量的大小来判断。如果新的版本的要大,那么肯定是l+1到r这些版本中存在一些字符串插入了这个深度为x的c,如果一样的话,就是不存在 ss + c…的字符串了,ss表示深度为x-1时候的结果(假设存在)。
总节点最大差不多是版本数 ∗ * ∗最长字符串的长度
插入和查询的时间复杂度是O(length)
#include <bits/stdc++.h>
using namespace std;
const int N = 5e4 + 5;
int ch[N * 33][2];//儿子指针
int sz[N * 33];//某一节点的某个字符的数量
int root[N * 33], tot;
void update(int last, int cur, int v) {
for (int i = 31; i >= 0; i--) {
ch[cur][1] = ch[last][1];
ch[cur][0] = ch[last][0];
sz[cur] = sz[last] + 1;
int j = 1 & (v >> i);
ch[cur][j] = ++tot;
cur = ch[cur][j];
last = ch[last][j];
}
sz[cur] = sz[last] + 1;
}
int query(int last, int cur, int v) {
int ans = 0;
for (int i = 31; i >= 0; i--) {
int j = !(1 & (v >> i));
if (sz[ch[cur][j]] > sz[ch[last][j]]) {
ans |= (1 << i);
last = ch[last][j];
cur = ch[cur][j];
} else {
last = ch[last][!j];
cur = ch[cur][!j];
}
}
return ans;
}
int main() {
int n, q;
scanf("%d%d", &n, &q);
for (int i = 1, x; i <= n; i++) {
scanf("%d", &x);
update(root[i - 1], root[i] = ++tot, x);
}
for (int i = 1, x, l, r; i <= q; i++) {
scanf("%d%d%d", &x, &l, &r);
printf("%d\n", query(root[l], root[r + 1], x));
}
return 0;
}