字典树
顾名思义
namespace Trie {
int Next[N][K], cnt, tag[N];
void Insert (char *s) {
int n = strlen(s + 1), now = 0;
for (int i = 1; i <= n; i++) {
int c = s[i] - 'a';
if (!Next[now][c]) Next[now][c] = ++cnt;
now = Next[now][c];
}
tag[now]++;
}
int Find (char *s) {
int n = strlen(s + 1), now = 0;
for (int i = 1; i <= n; i++) {
int c = s[i] - 'a';
if (!Next[now][c]) return false;
now = Next[now][c];
}
return tag[now];
}
}
0/1 Trie
在很多问题中
维护异或极值
考虑如下问题
由异或的性质
所以可以把所有从根开始的路径的异或和表示成一个数字
维护异或和
考虑如下问题
对于异或和的维护
其中 $sum$ 表示异或和
对于实现全局 $+1$ 操作
$0/1$ Trie 合并
与线段树合并类似
void Merge (int &p, int k) {
if (!p || !k) { p = p + k; return; }
tag[p] += tag[k];
Merge(l[p], l[k]), Merge(r[p], r[k]);
Push_up();
}
需要注意的是
可持久化 Trie 树
可持久化 Trie 的实现方式和可持久化线段树的方式是相似的
namespace Trie {
int rt[N << 5], Next[N << 5][2], cnt, tag[N << 5];
void Build (int &x, int y, int k, int pos) {
if (pos < 0) return;
x = ++cnt;
if (!pos) tag[x]++;
tag[x] += tag[y];
int d = (k & (1 << pos)) != 0;
Next[x][d ^ 1] = Next[y][d ^ 1];
Build(Next[x][d], Next[y][d], k, pos - 1);
}
int Query (int x, int y, int k, int pos) {
if (!x) return 0;
int d = (k & (1 << pos)) != 0;
return tag[x] - tag[y] + Query(Next[x][d], Next[y][d], k, pos - 1);
}
}
经典例题
[LG4735] 最大异或和
可持久化 $0/1$ Trie 模板题
考虑记所有的前缀和
[CF455B] A Lot of Games
显然直接建立 Trie 树
考虑设 $f$ 表示是否存在必胜策略
对 $f_{root}$ 和 $g_{root}$ 分四种情况讨论
[CF888G] Xor-MST
$\rm Trie+Boruvka$
考虑将所有树插入一个 Trie 中
发现一个性质
由于每个结点最多被加进 Trie 树 $\log n$ 次