树链剖分(轻重链剖分)算法笔记
仔细想想 自己第一次听说这个这个数据结构大概有两年半的时间了 然而一直不会.
不过现在再回头来看 发现其实也不是很麻烦
首先 在学树链剖分之前最好先把$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 }