可持久化字典樹學習筆記
阿新 • • 發佈:2020-12-10
引子
我們回憶一下,可持久化可以解決哪些問題?
我們通常用可持久化來解決區間詢問,將區間轉化成歷史版本
可持久化字典樹
加入我們有\(4\)個單詞\(cxc,cxd,cyc,cyt\)
我們用每一個版本代表插入之後的字典樹,我們就得到了如下的圖。
類似的,我們也可以得到可持久化01字典樹建樹的方法
例題
我們首先想到字首和,所以我們用一個01trie樹維護異或的字首和,然後考慮詢問
對於右端點,我們直接以\(root_r\)為根來進行查詢
關鍵是左端點,我們對於每一個節點,我們記錄一個\(last_i\)陣列,表示以\(i\)為根節點時,最大的版本編號。
這時我們在查詢的時候判斷一下,這個節點的\(last\)
我們就可以輕鬆的打出這樣程式碼
#include<iostream> using namespace std; const int N = 600010; int trie[N * 24][2], last[N * 24]; // last[i]表示結點以i為根節點的子樹中最大的版本編號 int s[N], root[N], n, m, tot; void insert(int i, int k, int p, int q) //第i個串s[i]的第k位, p是上一個版本, q是當前版本 { if(k < 0) { last[q] = i; //第i個字串出現的最後一個版本(版本最後會轉化成s序列的區間) return; } int c = s[i] >> k & 1; if(p) trie[q][c ^ 1] = trie[p][c ^ 1]; //只有0,1兩個孩子,q自己的孩子是c,則連到p的是c^1 trie[q][c] = ++tot; //新建結點 insert(i, k - 1, trie[p][c], trie[q][c]); last[q] = last[trie[q][c]]; return ; } int query(int cur, int val, int k, int left) //left左邊界 { if(k < 0) //二進位制數位到最低位 return s[last[cur]] ^ val; int c = val >> k & 1; if(last[trie[cur][c ^ 1]] >= left) return query(trie[cur][c ^ 1], val, k - 1, left); return query(trie[cur][c], val, k - 1, left); } int main() { cin >> n >> m; last[0] = -1; tot = 1; root[0] = tot; insert(0, 23, 0, root[0]); for(int i = 1; i <= n; i++) { int x; cin >> x; s[i] = s[i - 1] ^ x; root[i] = ++tot; insert(i, 23, root[i - 1], root[i]); //10^7次方的底數23 } for(int i = 1; i <= m; i++) { string op; cin >> op; if(op == "A") { int x; cin >> x; root[++n] = ++tot; s[n] = s[n - 1] ^ x; insert(n, 23, root[n - 1], root[n]); } else { int l, r, x; cin >> l >> r >> x; cout << query(root[r - 1], x ^ s[n], 23, l - 1) << "\n"; } } return 0; }