樹鏈剖分演算法整理

2021-10-10 13:11:39 字數 3780 閱讀 6651

樹鏈剖分可以把樹分成若干條鏈,從而維護樹上的路徑資訊。本質思想是把樹剖成可以用線性結構儲存的結構,然後可以資料結構維護。

分為三種:重鏈剖分、長鏈剖分、實鏈剖分。以下以重鏈剖分為主。

重鏈剖分可以將樹上的任意一條路徑劃分成不超過o(l

ogn)

o(logn)

o(logn

)條連續的鏈,每條鏈上的點深度互不相同(即是自底向上的一條鏈,鏈上所有點的 lca 為鏈的乙個端點)。

重鏈剖分還能保證劃分出的每條鏈上的節點 dfs 序連續,因此可以方便地用一些維護序列的資料結構(如線段樹)來維護樹上路徑的資訊。

相比之下,樹鏈剖分能解決的很多問題用樹上差分也能解決,兩者的關係近似於於線段樹和差分的關係。

重子節點:其子節點中子樹最大的子結點。如果有多個子樹最大的子結點,取其一。如果沒有子節點,就無重子節點。

輕子節點:除了重子節點的所有子節點。

重邊:從任意節點到重子節點的邊。

輕邊:從任意節點到輕子節點的邊。

重鏈:若干條首尾銜接的重邊,也包括了落單的節點。

如下圖 給出了具體的解釋(圖源oi-wiki)

為了剖出上述重鏈,我們需要維護如下資訊:

由兩次dfs即可處理出這些資訊,第一次處理前四項,後一次處理後四項。

處理**:

void

dfs1

(int u,

int f)}}

void

dfs2

(int u,

int f)

}

主要思想是重鏈內部的dfn序連續,所以我們只要以重鏈為基礎操作單位然後重鏈內部也可以單獨計算,和分塊之後的處理方法有點點相似的地方。

void

opchain

(int x,

int y)

//外界處理函式介面

solve

(dfn[top[x]

], dfn[x]);

x = fa[top[x]];

}if(dep[x]

> dep[y]

)swap

(x, y)

;solve

(dfn[x]

, dfn[y]);

}

#include

#include

#include

using

namespace std;

typedef

long

long ll;

const

int maxn =

1e5+5;

#define lson root << 1

#define rson root << 1 | 1

#define tr t[root]

int n, m, r, p;

vector<

int> g[maxn]

;int w[maxn]

, fa[maxn]

, son[maxn]

, dep[maxn]

, siz[maxn]

;int top[maxn]

, dfn[maxn]

, rnk[maxn]

;int tim;

struct segment_tree t[maxn <<2]

;void

pushdown

(int root)

}void

build

(int l,

int r,

int root)

int mid =

(l + r)

>>1;

build

(l, mid, lson)

;build

(mid +

1, r, rson)

; tr.val =

(t[lson]

.val + t[rson]

.val)

% p;

}void

modify

(int l,

int r,

int root,

int x)

int mid =

(tr.l + tr.r)

>>1;

pushdown

(root);if

(l <= mid)

modify

(l, r, lson, x);if

(r > mid)

modify

(l, r, rson, x)

; tr.val =

(t[lson]

.val + t[rson]

.val)

% p;

}int

query

(int l,

int r,

int root)

int mid =

(tr.l + tr.r)

>>1;

int ret =0;

pushdown

(root);if

(l <= mid) ret +

=query

(l, r, lson);if

(r > mid) ret +

=query

(l, r, rson)

;return ret % p;

}void

dfs1

(int u,

int f)}}

void

dfs2

(int u,

int f)

}void

mchain

(int x,

int y,

int z)

modify

(dfn[top[x]

], dfn[x],1

, z)

; x = fa[top[x]];

}if(dep[x]

> dep[y]

)swap

(x, y)

;modify

(dfn[x]

, dfn[y],1

, z);}

intqchain

(int x,

int y)

ret =

(ret +

query

(dfn[top[x]

], dfn[x],1

))% p;

x = fa[top[x]];

}if(dep[x]

> dep[y]

)swap

(x, y)

; ret =

(ret +

query

(dfn[x]

, dfn[y],1

))% p;

return ret;

}int

main()

dfs1

(r,0);

dfs2

(r, r)

;build(1

, n,1)

;while

(m--)}

return0;

}

樹鏈剖分 學習整理

在一棵樹上進行路徑的修改 求極值 求和 乍一看只要線段樹就能輕鬆解決,實際上,僅憑線段樹是不能搞定它的。我們需要用到一種貌似高階的複雜演算法 樹鏈剖分。樹鏈,就是樹上的路徑。剖分,就是把路徑分類為重鏈和輕鏈。重兒子 siz u 為v的子節點中siz值最大的,那麼u就是v的重兒子。輕兒子 v的其它子節...

樹鏈剖分 學習整理

重兒子 siz u 為v的子節點中siz值最大的,那麼u就是v的重兒子。輕兒子 v的其它子節點。重邊 點v與其重兒子的連邊。輕邊 點v與其輕兒子的連邊。重鏈 由重邊連成的路徑。輕鏈 輕邊。剖分後的樹有如下性質 性質1 如果 v,u 為輕邊,則siz u 2 siz v 性質2 從根到某一點的路徑上輕...

Query on a tree 樹鏈剖分整理

樹鏈剖分整理 樹鏈剖分就是把樹拆成一系列鏈,然後用資料結構對鏈進行維護。通常的剖分方法是輕重鏈剖分,所謂輕重鏈就是對於節點 u的所有子結點v,size v 最大的v與u 的邊是重邊,其它邊是輕邊,其中 size v 是以v 為根的子樹的節點個數,全部由重邊組成的路徑是重路徑,根據 上的證明,任意一點...