20190729 樹鏈剖分

2022-05-01 13:33:06 字數 3584 閱讀 8321

樹鏈剖分真是難寫極了:你需要先熟練掌握深搜、線段樹、倍增。。。(都是不好寫的東西啊,一手滑就wa)

一、簡介

樹鏈剖分通常用於維護靜態樹上路徑資訊的問題。樹鏈剖分的核心就是將數分為若干重鏈、輕鏈,然後把他們當做序列,按順序拼接起來,處理序列上的區間問題

二、相關量

fa【x】:x

在樹中的父親(用於倍增)

dep【x】:

x在樹中的深度(用於倍增)

size【x】:

x的子樹節點數(用於線段樹操作)

son【x】:

x的重兒子,

u  -->  son【u

】為重邊(用於建重邊)

top【x】:

x所在重路徑的頂部節點(深度最小)(用於倍增)

id【x】:x

nw【id【x

】】:線段樹中第id【

x】個位置對應樹中節點編號,即nw【

id【x】】

= x(用於查詢回答等各種操作)

三、流程

第一遍dfs處理出fa,

dep,

size

,son

第二遍dfs處理出

top,id,

nw(先處理重邊再處理輕邊)

int dep[maxn * 4],fa[maxn * 4],siz[maxn * 4],son[maxn * 4

];void dfs1(int x,int f,int

deep)

}}int id[maxn * 4],idx,top[maxn * 4],nw[maxn * 4

];void dfs2(int x,int

topf)

}

拆分成若干重路徑倍增處理成若干個線段樹上區間操作

int qrange(int ll,int

rr)

if(dep[ll] >dep[rr])swap(ll,rr);

ans = 0

; query(

1,n,id[ll],id[rr],1

); anss +=ans;

anss %=mod;

return

anss;

}void addrange(int ll,int rr,int

x)

if(dep[ll] >dep[rr])swap(ll,rr);

addchange(

1,n,id[ll],id[rr],x,1);}

int qson(int

ro)void addson(int ro,int

x)

線段樹操作

struct

node

tr[maxn * 8];int

ans;

void pushdown(int ro,int

len)

void build(int ll,int rr,int

ro)

else

}void query(int ll,int rr,int al,int ar,int

ro)

else

}

四、複雜度

o(log^2n)

五、題洛谷3384【模板】樹鏈剖分

1 #include2 #include3

using

namespace

std;

4#define maxn 200005

5int

n,m,r,mod;

6int to[maxn * 4],head[maxn * 4],nxt[maxn * 4

],cnt;

7int

a[maxn];

8void add(int u,intv)9

14int dep[maxn * 4],fa[maxn * 4],siz[maxn * 4],son[maxn * 4

];15

void dfs1(int x,int f,int

deep)

1632}33

}34int id[maxn * 4],idx,top[maxn * 4],nw[maxn * 4

];35

void dfs2(int x,int

topf)

3648}49

struct

node

50 tr[maxn * 8

];53

intans;

54void pushdown(int ro,int

len)

5564

void build(int ll,int rr,int

ro)65

72else

7380}81

void query(int ll,int rr,int al,int ar,int

ro)82

89else

909697}

98void addchange(int ll,int rr,int al,int ar,int x,int

ro)99

106else

107115

116}

117int qrange(int ll,int

rr)118

129if(dep[ll] >dep[rr])swap(ll,rr);

130 ans = 0

;131 query(1,n,id[ll],id[rr],1

);132 anss +=ans;

133 anss %=mod;

134return

anss;

135}

136void addrange(int ll,int rr,int

x)137

145if(dep[ll] >dep[rr])swap(ll,rr);

146 addchange(1,n,id[ll],id[rr],x,1

);147

}148

int qson(int

ro)149

154void addson(int ro,int

x)155

158int

main()

159169 dfs1(r,0,1

);170

dfs2(r,r);

171 build(1,n,1

);172

for(int i = 1; i <= m; i ++)

173182

else

if(k == 2)//

求x到y的最短路徑上所有節點之和

183188

else

if(k == 3)//

以x為根節點的子樹所有節點加z

189194

else

if(k == 4)//

求以x為根節點的子樹所有節點值之和

195200

}201

return0;

202 }

樹鏈剖分 樹鏈剖分講解

好了,這樣我們就成功解決了對樹上修改查詢邊權或點的問題。下面放上 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...

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

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

樹鏈剖分 樹剖換根

這是一道模板題。給定一棵 n 個節點的樹,初始時該樹的根為 1 號節點,每個節點有乙個給定的權值。下面依次進行 m 個操作,操作分為如下五種型別 換根 將乙個指定的節點設定為樹的新根。修改路徑權值 給定兩個節點,將這兩個節點間路徑上的所有節點權值 含這兩個節點 增加乙個給定的值。修改子樹權值 給定乙...