樹鏈剖分
(輕重鏈剖分)
chennnnnnnnnnnnn
要先具備的知識
1. 樹的基本構造
4. 線段樹或BIT(今天講概念,暫不提)
3. LCA(後面講題)
2. 樹的遍歷 dfs
Why using heavy light decomposition?
○ 給你一棵 N 個點的有權樹,執行 Q 此操作,操作有四種:
1. 將 x 到 y 路徑上的所有點權重 +z
2. 求 x 到 y 路徑上的所有點的權重和
○ 1 ≤ N, Q ≤ 1e5
○ 1 ≤ x, y ≤ N
○ 1 ≤ x ≤ 1e9
Why using heavy light decomposition?
○ 給你一棵 N 個點的有權樹,執行 Q 此操作,操作有四種:
1. 將 x 到 y 路徑上的所有點權重 +z
2. 求 x 到 y 路徑上的所有點的權重和
○ 1 ≤ N, Q ≤ 1e5
○ 1 ≤ x, y ≤ N
○ 1 ≤ x ≤ 1e9
若只要求所有點權重和
Why using heavy light decomposition?
○ 給你一棵 N 個點的有權樹,執行 Q 此操作,操作有四種:
1. 將 x 到 y 路徑上的所有點權重 +z
2. 求 x 到 y 路徑上的所有點的權重和
○ 1 ≤ N, Q ≤ 1e5
○ 1 ≤ x, y ≤ N
○ 1 ≤ x ≤ 1e9
若只要求所有點權重和
樹壓平
Why using heavy light decomposition?
○ 給你一棵 N 個點的有權樹,執行 Q 此操作,操作有四種:
1. 將 x 到 y 路徑上的所有點權重 +z
2. 求 x 到 y 路徑上的所有點的權重和
○ 1 ≤ N, Q ≤ 1e5
○ 1 ≤ x, y ≤ N
○ 1 ≤ x ≤ 1e9
若只要求所有點權重和
但若要在區間改值並維護 ?
Why using heavy light decomposition?
○ 給你一棵 N 個點的有權樹,執行 Q 此操作,操作有四種:
1. 將 x 到 y 路徑上的所有點權重 +z
2. 求 x 到 y 路徑上的所有點的權重和
○ 1 ≤ N, Q ≤ 1e5
○ 1 ≤ x, y ≤ N
○ 1 ≤ x ≤ 1e9
若只要求所有點權重和
但若要在區間改值並維護 ?
拆
爆
● 此時待修改的區間問題出現在樹上
● 我們嘗試將樹的結構強行分解成鏈的結構(也就是一堆序列)
● 如此一來,就能將樹上的區間問題
當作一般區間問題處理
常常是再套個資料結構處理區間
樹鏈剖分
如何實現
怎麼切
子樹大小為優先
子樹大小為優先
子樹大小為優先
子樹大小為優先
子樹大小為優先
子樹大小為優先
子樹大小為優先
該儲存的東西
・parent (父節點)
・sub_size (include 自己)
・root (鏈上的根,不在鏈上->n)
・dfsn
・next (鏈方向)或heavy_son
・depth
・value(看題目要求)
void dfs1(int now){
next[now]=-1;
sub_size[now]=1;//包括自己
for(int v:path[now]){
if(!depth[v]){
depth[v]=depth[now]+1;//深度
parent[v]=now;
dfs1(v);
sub_size[now]+=sub_size[v];
if(next[now]==-1||sub_size[v]>sub_size[next[now]]) next[now]=v;
//(若還沒指定或遇到更大的子樹) 將重兒子更新
}
}
}first time dfs 求出子樹大小、重兒子、parent、深度
子樹大小為優先
9
11
10
4
5
6
3
7
2
1
8
12
13
int time_cnt=0;
void dfs2(int now,int ancestor){
root[now]=ancestor;
dfsn[now]=++time_cnt;
if(next[now]==-1) return;
dfs2(next[now],ancestor);
for(int v:path[now]){
if(v!=parent[now]&&v!=next[now]) dfs2(v,v);
}
}second time dfs
把鍊做出來並編上樹鏈編碼
樹鏈剖分可以幹嘛
LCA
lowest common ancestor 最低共同祖先


最一般的想法
int lca(int l,int r){
if(depth[l]<depth[r]) swap(l,r);// 2 3
while(depth[l]>depth[r]) l=parent[l];
while(l!=r){
l=parent[l];
r=parent[r];
}
return l;
}time : O(n)
time : O(log n)
int lca(int a,int b){
while(root[a]!=root[b]){
if(d[root[a]]>d[root[b]]) a=p[root[a]];
else b=p[root[a]];
}
return (d[a]>d[b]? b:a);//深度小的為最低
}裸
RMQ
老母雞
Range Minimum/Maximum Query
樹鏈剖分
By chen0121
樹鏈剖分
- 30