字典树和可持久化字典树

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;
}
原文地址:https://www.cnblogs.com/nibabadeboke/p/13448609.html