朴素版Trie树
可持久化Trie树
可持久化Trie树,对于每个版本,需要将所有结点创建出来并与上一个版本建立联系。
优点:
从某个版本开始,能够遍历到该版本内的所有结点。
可以解决动态的异或问题。
例题:
题意:
给定一个非负整数序列 a a a,初始长度为 N N N。
有 M M M 个操作,有以下两种操作类型:
A x
:添加操作,表示在序列末尾添加一个数
x
x
x,序列的长度
N
N
N 增大
1
1
1。
Q l r
:询问操作,你需要找到一个位置
p
p
p,满足
l
≤
p
≤
r
l \le p \le r
l≤p≤r,使得:
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 最大,输出这个最大值。
Sol:
记录前缀异或和: s[i] = s[1] ^ s[2] ··· ^ s[i]
那么答案为:s[p - 1] ^ s[n] ^ x
,我们只需求出s[p - 1]
的最大值即可,那么问题就转化为了两个数异或最大。
Code:
#include <bits/stdc++.h>
using namespace std;
const int N = 6e5 + 10;
int n, m, s[N], idx;
int Trie[N * 24][2], version[24 * N];
int root[N];
// 非递归
void insert(int k, int p, int q)
{
version[q] = k;
for(int i = 30; i >= 0; -- i)
{
int v = s[k] >> i & 1;
if(p) Trie[q][v ^ 1] = Trie[p][v ^ 1];
Trie[q][v] = ++ idx;
version[Trie[q][v]] = k;
q = Trie[q][v], p = Trie[p][v];
}
}
int query(int p, int l, int c)
{
for(int i = 30; i >= 0; -- i)
{
int v = c >> i & 1;
if(version[Trie[p][v ^ 1]] >= l) p = Trie[p][v ^ 1];
else p = Trie[p][v];
}
return c ^ s[version[p]];
}
int main()
{
scanf("%d%d", &n, &m);
s[0] = 0;
version[0] = -1;
root[0] = ++ idx;
insert(0, 0, root[0]);
for (int i = 1; i <= n; i ++ )
{
int x;
scanf("%d", &x);
root[i] = ++idx;
s[i] = s[i - 1] ^ x;
insert(i, root[i - 1], root[i]);
}
while( m -- )
{
char op[2];
scanf("%s", op);
if(*op == 'A')
{
int x;
scanf("%d", &x);
n ++;
root[n] = ++ idx;
s[n] = s[n - 1] ^ x;
insert(n, root[n - 1], root[n]);
}
else {
int l, r, x;
scanf("%d%d%d", &l, &r, &x);
printf("%d\n", query(root[r - 1], l - 1, s[n] ^ x));
}
}
return 0;
}