樹鏈剖分原理與應用

2021-08-06 04:17:44 字數 3045 閱讀 8470

樹鏈剖分這個演算法我看了好多大神們的部落格,才慢慢領悟,希望我寫的過得去( ·  · )

眾所周知,維護區間資訊的題目可以用線段樹高效實現。類似於區間這樣一維的結構,在樹上維護兩點間的資訊【比如樹上兩點之間的最大值】也可以用線段樹嗎?

仔細想想,好像很難實現。因為線段樹維護的是鏈狀結構,而樹是一張圖。

但我們想想,如果能把一棵樹化為一條鏈,使得要節點間編號是一段一段的【也就是有些節點間的編號是連續的,但不是所有節點間都連續】,這樣就可以通過多次呼叫維護區間的線段樹來實現線段樹維護樹【兩點之間有幾個連續的段就呼叫幾次】

這就是樹鏈剖分要做的事情:把樹分成許多條鏈,對節點重新編號,化樹為鏈,以套用其他鏈狀資料結構

最好的就是剖分輕重鏈。

首先,我們先維護一些節點的資訊:

siz[u]      以u為根的子樹的大小

fa[u]       u的父親節點

dep[u]    u的深度

son[u]    u的重兒子

top[u]     u所在重鏈的頂端節點

id[u]       樹鏈剖分後u節點的編號

對於每乙個非葉節點,它所有兒子中siz最大的那個就是它的重兒子。

對於前4項,我們可以通過一次dfs完成

看**:

void dfs1(int u,int d,int f)

}

就這樣子,我們就「剖」完了(^_^)

**還是很容易理解的,多打打

為什麼放在一起?因為這兩個玩意原理一樣。

對於節點u和v,它們在乙個重鏈裡,當且僅當top[u]==top[v]成立

若它們不在乙個重鏈,我們不妨設top[u]的深度較大,那麼我們令u=fa[top[u]],繼續往上找。

在尋找過程中經過的路徑就是u和v之間的路徑,u最後到達的節點就是lca

這裡就不在贅述了【我懶。。。】

如題,已知一棵包含n個結點的樹(連通且無環),每個節點上包含乙個數值,需要支援以下操作:

操作1: 格式: 1 x y z 表示將樹從x到y結點最短路徑上所有節點的值都加上z

操作2: 格式: 2 x y 表示求樹從x到y結點最短路徑上所有節點的值之和

操作3: 格式: 3 x z 表示將以x為根節點的子樹內所有節點值都加上z

操作4: 格式: 4 x 表示求以x為根節點的子樹內所有節點值之和

輸入格式:

第一行包含4個正整數n、m、r、p,分別表示樹的結點個數、操作個數、根節點序號和取模數(即所有的輸出結果均對此取模)。

接下來一行包含n個非負整數,分別依次表示各個節點上初始的數值。

接下來n-1行每行包含兩個整數x、y,表示點x和點y之間連有一條邊(保證無環且連通)

接下來m行每行包含若干個正整數,每行表示乙個操作,格式如下:

操作1: 1 x y z

操作2: 2 x y

操作3: 3 x z

操作4: 4 x

輸出格式:

輸出包含若干行,分別依次表示每個操作2或操作4所得的結果(對p取模

輸入樣例#1:

5 5 2 24

7 3 7 8 0

1 21 5

3 14 1

3 4 2

3 2 2

4 51 5 1 3

2 1 3

輸出樣例#1:

2

21

時空限制:1s,128m

資料規模:

對於30%的資料: n≤10,m≤10 n \leq 10, m \leq 10 n≤

10,m

≤10對於70%的資料: n≤103,m≤103 n \leq ^3, m \leq ^3 n≤

10​3

​​,m

≤10​

3​​

對於100%的資料: n≤105,m≤105 n \leq ^5, m \leq ^5 n≤

10​5

​​,m

≤10​

5

經典的模板題,直接拍**,慢慢體會:

#include#include#include#includeusing namespace std;

const int maxn=100005,inf=200000000;

inline int read()

while(c>=48&&c<=57)

return out*flag;

}int n,m,rt,p;

int a[maxn];

int head[maxn],nedge=0;

struct edgeedge[2*maxn];

inline void build(int a,int b);

head[a]=nedge++;

edge[nedge]=(edge);

head[b]=nedge++;

}int siz[maxn],fa[maxn],son[maxn],dep[maxn],top[maxn],id[maxn],hash[maxn],cnt=0;

void dfs1(int u,int d,int f)

}void pd(int u,int l,int r)

void add(int u,int l,int r,int v)

else

void solve2(int u,int v)

void solve3(int u,int v)

void solve4(int u)

int main()

{ fill(head,head+maxn,-1);

n=read();

m=read();

rt=read();

p=read();

int a,b,cmd;

for(int i=1;i<=n;i++) a[i]=read();

for(int i=1;i

​​

樹鏈剖分原理

樹鏈剖分用一句話概括就是 把一棵樹剖分為若干條鏈,然後利用資料結構 樹狀陣列,sbt,splay,線段樹等等 去維護每一 條鏈,複雜度為o logn 那麼,樹鏈剖分的第一步當然是對樹進行輕重邊的劃分。定義size x 為以x為根的子樹節點個數,令v為u的兒子中size值最大的節點,那麼 u,v 就是...

樹鏈剖分 原理及簡單應用

我們可以把乙個樹剖分為若干條鏈,按照如下規則剖分可以得到的結論是 樹上任意兩點間的的鏈數一定不超過log nlog n logn 條。證明略。具體的 可以這樣實現 void dfs1 int x,int fa return 由於樹鏈剖分的作用是處理樹上的路徑問題,因此我們有必要通過樹鏈剖分將樹轉化為...

樹鏈剖分 樹鏈剖分講解

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