樹鏈剖分

(輕重鏈剖分)

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