樹鏈剖分講解

2022-05-13 11:47:53 字數 2930 閱讀 6220

題目:aragorn's story

題意:給一棵樹,每個結點都有初始的權值,有m個操作,分兩種:一是從x 結點到y 結點路上所有的結點權值+z或-z,二是問x結點的權值。

思路:

樹鏈剖分。

這是我學樹剖的第一題,建議還沒接觸過的夥伴,第一次學習的時候不要一直糾結理論,直接找一道模板題,然後找一篇ac**,直接理解,做完一題後,你就會發現理論其實也挺好理解的,樹剖也挺好學的。

樹剖中的新概念:重孩子、輕孩子、重鏈、輕鏈,後面解釋

fa[x]:x 結點的父結點

dep[x]:x 結點的深度

siz[x]:以x 結點為根的子樹的結點個數

son[x]:x 結點的重孩子,即x 所有孩子中siz 最大的那個(相對的,其他的為輕孩子)

結點x 和他重孩子的連邊叫作重邊,重邊組成的一條鏈叫作重鏈。

top[x]:x 所屬的重鏈的頭結點

xd[x]:和pos相反,表示線段樹中x 位置的結點是哪個

上面的陣列定義給出後,有過基礎的完全可以自己求出來了(下面ac**的dfs1、dfs2),至於求出來什麼用,再看下面。

比如現在要求:x 結點到y 結點路上所有結點的權值+1。

分兩種:1. 如果x、y 兩結點在同一條重鏈上,那麼他們路上的結點其實就是線段樹上連續的乙個區間[x, y],那麼像普通的線段樹區間更新似的便可以解決。2. 如果x、y 不在同乙個重鏈上,找到他們的鏈頭,也就是top[x]、top[y],判斷哪個深度大,選擇深度大的那個,假設為x,現在我們可以更新區間[top[x],x],然後x指向top[x]的父結點,再次判斷x、y是否同一重鏈。

單點查詢就不說了,和線段樹單點查詢一樣。

實現弄完了,我們來研究一下為什麼他可以快速解決該類問題,從區間更新那裡,我們可以看到,如果屬於同一條重鏈,那麼接下來的更新操作是線段樹操作,這個時間複雜度是logn大家都學過了。也就是說如果有可能慢,那就慢在屬於不同的重鏈,而且慢在必須一直跳(也就是說始終跳不到同一條重鏈),慢在重鏈的長度很短(一次只能跳一點)。如果始終沒跳到一條重鏈上,那麼跳的次數最多就是樹的高度,那麼會不會樹的高度很大而一次跳很短呢,答案是否定的,因為結點x 的重孩子是其所有孩子結點中siz 最大的那個,如果要跳的y 在重孩子那棵子樹,那麼邊x-son[x]是重邊,是可以跳過的,如果要跳的y 在輕孩子z那棵子樹,雖然x-z不是重邊,是不能跳過去的,但重孩子至少分去了一半的結點,這樣層層計算下來,最終時間複雜度也是logn,並不會出現跳的次數過多的情況。

ac**:

1 #include2 #include3 #include4

using

namespace

std;

5#define n 100010

6#define lson rt<<1

7#define rson rt<<1|1

8int

fa[n],dep[n],siz[n];

9int

son[n];

10 vectore[n];

11void dfs1(int rt,int f,int

h)1224}

25}26int

top[n],pos[n],xd[n],po;

27void dfs2(int rt,int

org)

2839}40

struct

node

4148

};49 node v[n<<2

];50

intnum[n];

5152

void build(int l,int r,int

rt)53

62build(l,v[rt].mid(),lson);

63 build(v[rt].mid()+1

,r,rson);

64 v[rt].w = v[lson].w+v[rson].w;65}

66void update(int val,int l,int r,int

rt)67

74if

(v[rt].c)

7582

int mid=v[rt].mid();

83if(l<=mid) update(val,l,r,lson);

84if(r>mid) update(val,l,r,rson);

85 v[rt].w = v[lson].w+v[rson].w;86}

87void change(int x,int y,int

val)

8895

if(dep[x]>dep[y]) swap(x,y);

96 update(val,pos[x],pos[y],1

);97}98

int query(int rt,int

val)

99110

int mid=v[rt].mid();

111int ret=0

;112

if(val<=mid) ret=query(lson,val);

113else ret=query(rson,val);

114 v[rt].w = v[lson].w+v[rson].w;

115return

ret;

116}

117int

main()

118129

130for(int i=1;i)

131136 dfs1(1,0,0

);137 dfs2(1,1

);138 build(1,n,1

);139

while(m--)

140148

else

149154

}155

}156

return0;

157 }

樹鏈剖分 樹鏈剖分講解

好了,這樣我們就成功解決了對樹上修改查詢邊權或點的問題。下面放上 vector v maxn int size maxn dep maxn val maxn id maxn hson maxn top maxn fa maxn 定義 int edge 1,num 1 struct tree e ma...

數鏈剖分基礎講解

在一棵樹上進行路徑的修改 求極值 求和 乍一看只要線段樹就能輕鬆解決,實際上,僅憑線段樹是不能搞定它的。我們需要用到一種貌似高階的複雜演算法 樹鏈剖分。樹鏈,就是樹上的路徑。剖分,就是把路徑分類為重鏈和輕鏈。重兒子 siz u 為v的子節點中siz值最大的,那麼u就是v的重兒子。輕兒子 v的其它子節...

演算法入門 樹鏈剖分 輕重鏈剖分

目錄 3.0 求 lca 4.0 利用資料結構維護資訊 5.0 例題 參考資料 資料結構入門 線段樹 發表於 2019 11 28 20 39 dfkuaid 摘要 線段樹的基本 建樹 區間查詢 單點修改 及高階操作 區間修改 單點查詢 區間修改 區間查詢 標記下傳 標記永久化 閱讀全文 樹鏈剖分用...