字典樹和可持久化字典樹
阿新 • • 發佈:2020-08-06
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\) 都是以 \(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;
}