Trie
(Trie)的结构非常好懂,我们用(delta(u,v))表示结点(u)的(v)字符指向的下一个结点,或着说是结点(u)代表的字符串后面添加一个字符(c)形成的字符串的结点。((c)的取值范围和字符集大小有关,不一定是 (0 sim 26)。)
(Trie)的简图:
(Code)
插入
void insert(int x) //插入x
{
for (int i = 30, u = 1; i >= 0; --i)
{
int c = ((x >> i) & 1);
if (!ch[u][c]) ch[u][c] = ++tot;
u = ch[u][c];
}
}
查询
void get(int x) {
int res = 0;
for (int i = 30, u = 1; i >= 0; --i) {
int c = ((x >> i) & 1);
if (ch[u][c ^ 1]) {
u = ch[u][c ^ 1];
res = ...... //根据题意求答案
} else
u = ch[u][c];
}
ans = std::max(ans, res);
}
可持久化字典树
可持久化 (Trie) 的方式和可持久化线段树的方式是相似的,即每次只修改被添加或值被修改的节点,而保留没有被改动的节点,在上一个版本的基础上连边,使最后每个版本的 (Trie) 树的根遍历所能分离出的 (Trie) 树都是完整且包含全部信息的。
大部分的可持久化 (Trie) 题中,(Trie) 都是以 (01-Trie) 的形式出现的。
(Code)
void update(int u,int v,int x)//新建版本u,上一个版本是v,插入x
{
for (int i = 30; i >= 0; i--)
{
int c = (x >> i) & 1;
sum[u] = sum[v] + 1;//sum[x] 代表连x的边有多少个数经过
ch[u][c ^ 1] = ch[v][c ^ 1];
ch[u][c] = ++tot;
u = ch[u][c],v = ch[v][c];
}
sum[u] = sum[v] + 1;
}
查询区间,只需要利用前缀和和差分的思想,用两棵前缀 (Trie) 树(也就是按顺序添加数的两个历史版本)相减即为该区间的线段树。再利用动态开点的思想,不添加没有计算过的点,以减少空间占用。
int query(int u,int v,int x)//求区间[u + 1,v]关于x的答案
{
int res = 0;
for (int i = 30; i >= 0; i--)
{
int c = (x >> i) & 1,p = sum[ch[v][c ^ 1]] - sum[ch[u][c ^ 1]];
if (p) res = ...........;//根据题意求答案
else u = ch[u][c],v = ch[v][c];
}
return res;
}