輕重鏈剖分 學習筆記

2021-10-23 16:50:59 字數 3407 閱讀 6377

題目傳送門

這道題目要求在樹上修改和查詢點一條鏈上或者是一棵子樹的點的權值。

考慮使用lca,但是不能使用倍增的解法(倍增只能查詢不能修改),所以我們要使用一種新的演算法——輕重鏈剖分。

建議先學完lca在來看這篇文章。

這裡列出一些必要的定義:

重兒子:子節點最多的乙個兒子。

輕兒子:乙個節點的兒子除重兒子之外的兒子。

重邊:一段為重兒子的邊。

輕邊:除重邊以外的邊。

重鏈:由重邊組成的鏈,由輕兒子為起點。

預處理預處理由兩次dfs組成。

第一次dfs需要處理以下陣列:

f a,

son,

siz,

dfa,son,siz,d

fa,son

,siz

,d分別代表節點的 父親,重兒子,子樹節點數,深度。

第二次dfs需要處理以下陣列:

i d,

top,

aid,top,a

id,top

,a分別代表節點的新編號(按照dfs達到的順序),這個節點所在的重鏈的頂端,新編號的點權,注意一定要先處理重兒子並且使用dfs。原因後面講。

修改怎麼修改或者查詢一條鏈的點權呢?

我們可以讓更深的點沿著重鏈向上跳,直到兩個點處於同一條重鏈上就可以了。

然後我們會發現,如果先處理重兒子並且使用dfs的時候,這樣一條重鏈上的節點和一棵子樹的節點的編號是連續的,我們就可以使用線段樹來解決這個問題了。

而處理一棵子樹權值的時候,我們只要將 idi

id_i

idi​

到 idi+

sizi

−1

]id_i+siz_i-1]

idi​+s

izi​

−1] 這段區間進行修改或者是查詢就可以了。

因為修改的點的 idid

id都是連續的,所以我們就可以使用線段樹**來解決了。

複雜度處理一條鏈的複雜度是 θ

(log⁡2

n)

\theta\left(\log^2n\right)

θ(log2n)

,處理子樹的複雜度是 θ

(log⁡n

)\theta\left(\log n\right)

θ(logn) 。

**這裡用遞迴式線段樹來解決這個問題。

#include

#define maxn 100039

#define emaxn 200039

using

namespace std;

typedef

long

long ll;

int n,t,root;

ll mod;

int head[maxn]

,nex[emaxn]

,to[emaxn]

,k;#define add(x,y) nex[++k]=head[x];\

head[x]=k;\

to[k]=y;

int tmp,x,y;

ll z;

//鏈式前向星

int w[maxn]

,a[maxn]

;int siz[maxn]

,son[maxn]

,d[maxn]

,fa[maxn]

;//size是linux保留字

int id[maxn]

,top[maxn]

,cnt;

void

dfs1

(int num,

int pre)

return;}

void

dfs2

(int num,

int topf)

//預處理

//以下為線段樹

int l,r;

ll c;

ll sum[maxn<<2]

,f[maxn<<2]

;voidup(

int rt)

void

build

(int l,

int r,

int rt)

int m=

(l+r)

>>1;

build

(l,m,rt<<1)

;build

(m+1

,r,rt<<1|

1);up

(rt);}

void

down

(int ln,

int rn,

int rt)

return;}

void

update

(int l,

int r,

int rt)

int m=

(l+r)

>>1;

down

(m-l+

1,r-m,rt);if

(m>=l)

update

(l,m,rt<<1)

;if(mupdate

(m+1

,r,rt<<1|

1);up

(rt);}

ll find

(int l,

int r,

int rt)

//以上為線段樹

void

init()

void

swap

(int

&x,int

&y)//交換

void

updater

(int u,

int v,ll c)

if(d[u]

)swap

(u,v)

; l=id[v]

; r=id[u]

;update(1

,n,1);

return;}

ll findr

(int u,

int v)

if(d[u]

)swap

(u,v)

; l=id[v]

; r=id[u]

; ans+

=find(1

,n,1);

return ans;

}void

updates

(int rt,ll c)

ll finds

(int rt)

intmain()

init()

;while

(t--

)else

if(tmp==2)

else

if(tmp==3)

else

}return0;

}

輕重鏈剖分

樹鏈剖分是一種將樹轉化為一條鏈的演算法,通常和線段樹,樹狀陣列,dp等針對鏈的演算法結合使用。正如題目所說,本文只講輕重鏈剖分 預處理首先扔出乙個定理 樹中任意一條路徑均可以拆分成一條鏈上 o log n 個連續區間。證明 構造 乙個dfs序就行了 但是實際 實現中,一般都是優先遍歷重兒子,這樣遍歷...

輕重鏈剖分

目錄樹剖完就是線段樹題了qwq 沒了題外話 鴿說叫 he y light decomposition 或 he y path decomposition 正確叫法 不是 這是真的 乙個節點子樹大小最大的兒子叫重兒子 節點到重兒子的邊叫重邊 一堆重邊叫重鏈 重兒子優先 dfs,於是重鏈連續,每條鏈可以...

模板 輕重鏈剖分

目錄後記 模板 輕重鏈剖分 傳送門總的來說,就是乙個不難理解,碼量 的東西 推幾篇題解,講得不錯 線段樹 必備 倍增lca 可以幫助理解,不會應該也可以 鏈式前向星 存圖,不會有人不會吧 重兒子 子樹結點最多的兒子 重邊 某個點到它的重兒子連成的邊 重鏈 重邊連成的鏈 輕兒子 除重兒子外的其它兒子 ...