樹鏈剖分(輕重鏈剖分)算法筆記


仔細想想 自己第一次聽說這個這個數據結構大概有兩年半的時間了 然而一直不會.

不過現在再回頭來看 發現其實也不是很麻煩

首先 在學樹鏈剖分之前最好先把$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 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM