樹鏈剖分筆記 (戳)

2021-08-08 19:57:43 字數 3071 閱讀 2759

對於樹鏈剖分,看到大佬紛紛各種躺切內心很是惶恐,於是跪著請教大佬之後把自己的一點感悟發出來以防以後忘了qwq

指一種對樹進行劃分的演算法,它先通過輕重邊剖分將樹分為多條鏈,保證每個點屬於且只屬於一條鏈,然後再通過資料結構(樹狀陣列、sbt、splay、線段樹等)來維護每一條鏈

常見的剖分的方法是輕重樹鏈剖分(也叫啟發式剖分)

·第一步是對樹進行輕重邊的劃分。

首先說一下什麼叫重邊

重邊:某個節點x到孩子節點形成的子樹中節點數最多的點child之間的邊,由定義發現除了葉子節點其他節點只有一條重邊

因此定義size[x]為以x為根的子樹節點個數,令v為u的兒子中size值最大的節點,那麼(u,v)就是重邊,其餘邊為輕邊。

·進一步剖分過程分為兩次dfs

第一次dfs就是找重邊,也就是記錄下所有的重邊。

程式具體操作:

首先記錄深度,更新父親,初始化兒子數,然後再在向外擴充套件時更新兒子數,按照定義記錄重兒子邊

·然後第二次dfs就是連線重邊形成重鏈這是一般的樹鏈剖分套路

對於本題來講in函式為插入一點,在處理求和dosum函式時要理解

過程是找同一條鏈,讓更深的往上跳,直到來到了一條鏈上,然後處理求和

另外陣列有點多,不好好看可能有點暈(¦3」∠)_(:з」∠)_

size陣列,用來儲存以x為根的子樹節點個數

top陣列,用來儲存當前節點的所在鏈的頂端節點

son陣列,用來儲存重兒子

dep陣列,用來儲存當前節點的深度

fa陣列,用來儲存當前節點的父親

可以用二維陣列鄰接表鏈式前向星來表示邊,聽說用鄰接表or鏈式前向星來搞邊是比較好的,對於我主要還是對鏈式前向星比較熟悉emmmmmm

搞完了剖分基本就是套線段樹之類資料結構的來進一步實現搞事,這個嘛本蒟蒻需要繼續鑽研,不敢亂說23333

以codevs 2460 樹的統計為例(寫得醜不要嫌棄qwq)

附上乙份中間沒空格的樣例輸入

4

1 2

2 3

4 1

4 2 1 3

12 qmax 3 4

qmax 3 3

qmax 3 2

qmax 2 3

qsum 3 4

qsum 2 1

change 1 5

qmax 3 4

change 3 6

qmax 3 4

qmax 2 4

qsum 3 4

輸出:

4

1 2

2 10

6 5

6 5

16

**如下:

#include#include#include#define ls p<<1

#define rs p<<1|1

#define mid (shu[p].l+shu[p].r >>1)

#define ri register int

using namespace std;

const int sz = 300010;

int pos[sz],son[sz],top[sz],size[sz];

int dep[sz],fa[sz],fir[sz],nxt[sz];

int w[sz];

int tot,n,q,cnt,x,y,num;

char ch[15];

struct nodeshu[sz<<2];

struct edl[sz<<1];

inline void read(int &x)

while(ch >= '0' && ch <= '9')

if(flag)

x *= -1;

}inline void add(int f,int t)

; nxt[tot]=fir[f];

fir[f]=tot;

}inline void build(int p,int l,int r)

int midd = l+r>>1;

build(ls,l,midd);

build(rs,midd+1,r);

shu[p].sum=shu[ls].sum+shu[rs].sum;

shu[p].max=max(shu[ls].max,shu[rs].max);

}void dfs1(int k,int f,int d)

}void dfs2(int k,int tot)

}void in(int p,int x,int v)

if(x<=mid)

in(ls,x,v);

else

if(x>mid)

in(rs,x,v);

shu[p].sum=shu[ls].sum+shu[rs].sum;

shu[p].max=max(shu[ls].max,shu[rs].max);

}int qsum(int p,int l,int r)

int qmax(int p,int l,int r)

int dosum(int x,int y)

if(pos[x]>pos[y])

swap(x,y);

ans+=qsum(1,pos[x],pos[y]);

return ans;

}int domax(int x,int y)

if(pos[x]>pos[y])

swap(x,y);

ans=max(ans,qmax(1,pos[x],pos[y]));

return ans;

}int main()

else

}return

0;

}

筆記 樹鏈剖分

樹鏈剖分 對於每個結點 連線子節點多的那個子樹的邊叫做重邊 重邊連起來成鏈 叫重鏈 不止是只有一條重鏈 使用線段樹或者其他的資料結構存重鏈 用此資料結構進行操作 兩個dfs操作 第乙個dfs 求出重鏈 深度dep陣列 父節點fa陣列 和每個結點的子結點個數size陣列 知道了size才可以求出重鏈 ...

筆記 樹鏈剖分

洛谷 樹鏈剖分模板 前置芝士 鏈式前向星,線段樹,dfs序 這裡寫的是重鏈剖分。參考部落格指盜圖 x正義小學生x 樹鏈剖分可以把一棵樹 投影 到乙個序列上,然後用線段樹維護一些東西。通過重兒子的性質來保證時間複雜度。我們首先使用兩次dfs進行預處理,將樹投影到序列上。對於乙個有兒子的節點,我們定義它...

樹鏈剖分 樹鏈剖分講解

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