詳解樹鏈剖分

2021-08-02 12:52:03 字數 2836 閱讀 5292

樹鏈剖分,顧名思義為將鏈剖開分成多條。

當我們想要修改樹上一條路的值或求值時,我們暴力只能用乙個個修改,這是非常慢的。這時,我們就要想乙個辦法,資料結構?但是資料結構我們都需要連續修改,可是樹上路徑的編號是不連續的。於是我們想了乙個辦法。

我們先定義

fa[x]為x的父親

dep[x]為x的深度

size[x]為以x為根的子樹的節點個數

幾個名詞:

1.重邊(乙個節點的兒子中size最大的點與這個點之間的邊)

2.重兒子 x的重兒子記為son[x](乙個節點重邊連線的兒子)

3.輕邊(乙個節點除重兒子外與其他兒子鏈結的邊)

4.輕兒子 (x除重兒子外的兒子)

5.重鏈(由重邊連線而成的鏈)

6.輕鏈(由輕邊連線而成的鏈)

然後我們再定義

top[x]為x所在的重鏈中深度最小的節點(若x不為fa[x]的重兒子則top[x]=x,即自己乙個點形成一條重鏈)

我們舉個例子,在這棵樹中,son[1]=2(因為size[2]比size[3]和size[4]大)。

1,2,6,9,11構成一條重鏈;3,7,10構成一條重鏈。

top[1]=top[2]=top[6]=top[9]=top[11]=1

top[3]=top[7]=top[10]=3

top[5]=5 top[4]=4 top[8]=8

然後又這樣一條性質,從根到任意一節點的路徑上輕鏈,重鏈的個數都不大於logn(n為節點個數)。

現在我們將一條重鏈上連續標號,建立新的標號(這是為了可以在資料結構中連續修改)。

我們dfs遍歷,優先走重兒子,則上面那個圖的新標號tid為:

tid[1]=1 tid[2]=2 tid[6]=3 tid[9]=4 tid[11]=5

tid[5]=6

tid[3]=7 tid[7]=8 tid[10]=9

tid[8]=10

tid[4]=11

我們舉個例子:

上圖中從10~11,即u=10,v=11,因為top[v]>top[u],那麼我們先使u跳到fa[top[10]]=1,然後u和v就在一條重鏈上了,那麼我們就可以直接修改了。

bzoj1036[zjoi2008]樹的統計count

這題是樹鏈剖分裸題,下面給出此題**以供參考和理解。

#include

#include

#include

#include

#include

#define n 30500

using namespace std;

int n,tot,lable,next[n*2],head[n*2],tree[n*2],a[n],m,fa[n],dep[n],son[n],size[n],tid[n],number[n];

int top[n],max[n*8],sum[n*8];

bool visit[n];

void add(int x,int y)

void dfs(int x,int depth,int father)

}}void dfs1(int x,int ancestor)

void up(int x)

void build(int l,int r,int

id) int mid=(l+r)/2;

build(l,mid,id*2);

build(mid+1,r,id*2+1);

up(id);

}void change(int x,int l,int r,int

id,int d)

int mid=(l+r)/2;

change(x,l,mid,id*2,d);

change(x,mid+1,r,id*2+1,d);

up(id);

}int query(int x,int y,int

id,int l,int r,int q)

if (x<=l&&r<=y)

int mid=(l+r)/2;

if (q==0) return max(query(x,y,id*2,l,mid,q),query(x,y,id*2+1,mid+1,r,q));

else

return query(x,y,id*2,l,mid,q)+query(x,y,id*2+1,mid+1,r,q);

up(id);

}int query(int x,int y,int q)

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

if (q==0) ans=max(ans,query(tid[x],tid[y],1,1,n,0));

else ans+=query(tid[x],tid[y],1,1,n,1);

return ans;

}int main()

for (int i=1;i<=n;i++) scanf("%d",&a[i]);

scanf("%d",&m);

for (int i=1;i<=n;i++) visit[i]=false;

dfs(1,1,0);

for (int i=1;i<=n;i++) visit[i]=false;

dfs1(1,1);

build(1,n,1);

char s[100];

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

return

0;}

以下鏈結是一些關於樹鏈剖分題的部落格鏈結

鏈結

樹鏈剖分詳解

樹鏈剖分定義 只是把一棵樹拆成鏈來處理而已,即將樹上的某些段一起通過資料結構優化進行處理來降低複雜度。樹鏈剖分相關定義 重兒子 ve v 為 u 的子節點中ve 值最大的,那麼 v 就是 u的重兒子 將子樹中最長的那一條鏈一起處理來降低複雜度 輕兒子 u 除了重兒子的其它子節點。重邊 點 u與其重兒...

樹鏈剖分詳解

重兒子 對於每乙個非葉子節點,它的兒子中 以那個兒子為根的子樹節點數最大的兒子 為該節點的重兒子 ps 感謝 shzr大佬指出我此句話的表達不嚴謹qwq,已修改 輕兒子 對於每乙個非葉子節點,它的兒子中 非重兒子 的剩下所有兒子即為輕兒子 葉子節點沒有重兒子也沒有輕兒子 因為它沒有兒子。重邊 乙個父...

樹鏈剖分詳解

樹鏈剖分,說白了就是一種讓你 不得不強行增加1k的資料結構 dms 個人理解 1 joy 證明出題人非常毒瘤 可以非常友 bao 好 li 的解決一些樹上問題 grimacing 樹鏈剖分的思想比較神奇 它的思想是 把一棵樹拆成若干個不相交的鏈,然後用一些資料結構去維護這些鏈 那麼問題來了 首先明確...