仔細想想 自己第一次聽說這個這個數據結構大概有兩年半的時間了 然而一直不會.
不過現在再回頭來看 發現其實也不是很麻煩
首先 在學樹鏈剖分之前最好先把$LCA$ 樹形$DP$ 以及$dfs$序 這三個知識點學了
如果這三個知識點沒掌握好的話 樹鏈剖分難以理解也是當然的
-------------------------------------------------------------------------------------------
樹鏈剖分通常用於處理樹的形態不變 但點權/邊權需要修改查詢的題目
在選定一個點作為根后 我們來對這棵樹進行操作
第一步
從根開始進行一遍遍歷$($通常用$dfs)$ 這一遍遍歷需要統計以下信息
父親節點$fa$ 當前節點深度$d$ 子樹大小$sz$ 這些顯然是在學習前置知識時已經用得比較熟練了的
然后 在這一遍遍歷中 我們需要再求當前節點的重兒子$son$
重兒子的定義是 當前節點的子節點中 子樹大小最大的那個 $($如果有多個任取一個$)$
其余的就是輕兒子了
另外所有節點與其重/輕兒子的連邊稱為重/輕邊
把連續的重邊定義為重鏈
我們會發現這樣一個性質
從任一節點向根節點走 走過的重鏈和輕邊的條數都是$log$級別以內的
證明如下:
由於任一輕兒子對應的子樹大小要小於父節點所對應子樹大小的一半
因此從一個輕兒子沿輕邊向上走到父節點后 所對應的子樹大小至少變為兩倍以上
經過的輕邊條數自然是不超過$log_2N$的
然后由於重鏈都是間斷的 $($連續的可以合成一條$)$
所以經過的重鏈的條數是不超過輕邊條數$+1$的
因此經過重鏈的條數也是$log$級別的
綜合可知原命題得證
從輕邊向上走顯然每條輕邊都可以做到$O(1)$向上走
而從重鏈向上走要做到每條重鏈只用$O(1)$就必須額外做一些處理
第二步
利用第一遍遍歷得到的信息 我們再進行一遍遍歷$($需用$dfs)$
對於每個重兒子 要求出沿重鏈向上走走到頂端的點的位置$top$
這個$top$顯然是和父節點的一樣的
對於每個輕兒子 由於向上走的重鏈不存在 我們可以令$top$為自身
現在從重鏈向上走都只需要$O(1)$了
不過修改的復雜度仍然不靠譜
對於兩點之間的修改和詢問操作 輕邊我們可以暴力直接修改詢問 重鏈則必須結合一些數據結構進行以保證效率
這個時候 我們或許會回想起學$dfs$序的時候 我們將樹上的點根據dfs序映射到了一維數組上
從而可以利用線段樹等數據結構對在$dfs$序上連續的一段區間進行修改和詢問
因此 為了能夠用線段樹等數據結構進行維護 我們必須將同一條重鏈上的點映射到一個連續的區間
這個操作並不復雜 我們只需在對每個點$dfs$時先遍歷到它的重兒子 最后重鏈上的點映射到一維數組里便是連續的
做完這兩個步驟后 樹鏈剖分的核心部分就結束了
不過注意兩個點向上走的時候 不能走得超過這兩個點的$LCA$
這里的具體操作最好獨立思考一下 對比倍增$LCA$的寫法會更好理解
看到這里 大家大概也能反應到樹鏈剖分的復雜度是$O(NlogN + Qlog^{2}N)$
以下是$SPOJ\ QTREE$的代碼 僅供參考
1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 #include <algorithm> 5 using namespace std; 6 const int N = 1e4 + 10; 7 int firste[N], nexte[N << 1], v[N << 1], w[N << 1]; 8 int fa[N], d[N], sz[N], son[N]; 9 int id[N], top[N], up[N], down[N]; 10 int mx[N << 2]; 11 char s[20]; 12 int t, n, e, cnt; 13 void build(int x, int y, int z) 14 { 15 nexte[++e] = firste[x]; 16 firste[x] = e; 17 v[e] = y; 18 w[e] = z; 19 } 20 void dfs1(int u) 21 { 22 sz[u] = 1; 23 for(int p = firste[u]; p; p = nexte[p]) 24 if(v[p] != fa[u]) 25 { 26 fa[v[p]] = u; 27 d[v[p]] = d[u] + 1; 28 down[p >> 1] = v[p]; 29 dfs1(v[p]); 30 sz[u] += sz[v[p]]; 31 if(sz[v[p]] > sz[son[u]]) 32 son[u] = v[p]; 33 } 34 } 35 void dfs2(int u) 36 { 37 id[u] = ++cnt; 38 if(son[u]) 39 { 40 top[son[u]] = top[u]; 41 dfs2(son[u]); 42 } 43 for(int p = firste[u]; p; p = nexte[p]) 44 if(v[p] != fa[u] && v[p] != son[u]) 45 { 46 top[v[p]] = v[p]; 47 dfs2(v[p]); 48 } 49 } 50 void update(int x, int L, int R, int tl, int tr, int y) 51 { 52 if(L == tl && tr == R) 53 { 54 mx[x] = y; 55 return; 56 } 57 int mid = (tl + tr) >> 1; 58 if(L < mid) 59 update(x << 1, L, R, tl, mid, y); 60 else 61 update(x << 1 | 1, L, R, mid, tr, y); 62 mx[x] = max(mx[x << 1], mx[x << 1 | 1]); 63 } 64 int query(int x, int L, int R, int tl, int tr) 65 { 66 if(L == R) 67 return 0; 68 if(L <= tl && tr <= R) 69 return mx[x]; 70 int mid = (tl + tr) >> 1, re = 0; 71 if(L < mid) 72 re = max(re, query(x << 1, L, R, tl, mid)); 73 if(mid < R) 74 re = max(re, query(x << 1 | 1, L, R, mid, tr)); 75 return re; 76 } 77 int main() 78 { 79 scanf("%d", &t); 80 while(t--) 81 { 82 e = 1; 83 memset(firste, 0, sizeof firste); 84 memset(fa, 0, sizeof fa); 85 memset(d, 0, sizeof d); 86 memset(sz, 0, sizeof sz); 87 memset(son, 0, sizeof son); 88 memset(id, 0, sizeof id); 89 memset(top, 0, sizeof top); 90 memset(up, 0, sizeof up); 91 memset(down, 0, sizeof down); 92 memset(mx, 0, sizeof mx); 93 cnt = 0; 94 top[1] = 1; 95 scanf("%d", &n); 96 int x, y, z; 97 for(int i = 1; i < n; ++i) 98 { 99 scanf("%d%d%d", &x, &y, &z); 100 build(x, y, z); 101 build(y, x, z); 102 } 103 dfs1(1); 104 dfs2(1); 105 for(int i = 2; i < (n << 1); i += 2) 106 { 107 int l = id[v[i]], r = id[v[i ^ 1]]; 108 if(l > r) 109 swap(l, r); 110 if(r - l == 1) 111 update(1, l, r, 1, cnt, w[i]); 112 else 113 { 114 if(id[v[i]] > id[v[i ^ 1]]) 115 up[v[i]] = w[i]; 116 else 117 up[v[i ^ 1]] = w[i]; 118 } 119 } 120 while(scanf("%s", s), s[0] != 'D') 121 { 122 if(s[0] == 'Q') 123 { 124 int ans = 0, x, y; 125 scanf("%d%d", &x, &y); 126 while(top[x] != top[y]) 127 { 128 if(d[top[x]] < d[top[y]]) 129 swap(x, y); 130 ans = max(ans, query(1, id[top[x]], id[x], 1, cnt)); 131 x = top[x]; 132 ans = max(ans, up[x]); 133 x = fa[x]; 134 } 135 if(d[x] < d[y]) 136 swap(x, y); 137 ans = max(ans, query(1, id[y], id[x], 1, cnt)); 138 printf("%d\n", ans); 139 } 140 else 141 { 142 int x, y; 143 scanf("%d%d", &x, &y); 144 x = down[x]; 145 if(top[x] != x) 146 update(1, id[fa[x]], id[x], 1, cnt, y); 147 else 148 up[x] = y; 149 } 150 } 151 } 152 return 0; 153 }