演算法雜談 02 樹鏈剖分

2021-06-26 01:10:47 字數 985 閱讀 5241

我們知道,對一段連續區間進行修改/求和/求極值……操作,可用線段樹等資料結構進行維護。那麼,如果這不是乙個區間,而是一棵樹,我們又能怎樣解決?此時我們就用到了樹鏈剖分——這一專為此類的問題而生的演算法。

樹鏈剖分的原理是將一棵樹劃分為幾部分並分別對映到一維區間中,這樣對樹進行操作就可以轉化為對一維區間進行操作並用我們所熟悉的樹狀陣列、線段樹、splay……維護。

最常用的樹鏈剖分方法是輕重邊樹鏈剖分——將一棵樹中的邊分為

輕重兩類。首先對於每乙個非葉子節點的節點,規定其重兒子為它的子節點中以其為根的子樹節點總數最大的乙個,上述節點與其重兒子之間的連邊為一條重邊,與其他子節點之間的連邊為輕邊,於是整棵樹中每一條首尾相連的重邊都構成了一條重鏈,每一條輕邊都構成了一條輕鏈。

樹鏈剖分過程**實現:

首先對樹進行第一遍dfs,求出每乙個節點的父節點、深度、以其為根的子樹的節點總數和重兒子,再進行第二遍dfs,即可求出每乙個節點所在重鏈的頂端和它在區間中對應的位置、區間中每一點對應的樹中的節點編號。

樹鏈剖分過程c++**實現:

void dfs(int x,int d)

}void create(int x,int d)

歷經兩遍dfs後整棵樹便剖分完成了,每一條重鏈上的點都依次被對映到連續區間上,若對一條重鏈進行區間操作,則可使用線性結構維護,將時間由o(n)降到了o(logn)!另外,樹鏈剖分的美妙性質:任意一點到根節點上的路徑上的鏈的個數不超過logn條,從而對任意兩點進行操作可通過列舉路徑上重鏈進行操作而快速實現。下面列出求兩點間路徑上的點權和c++**:

int findsum(int x,int y)

上面**中query_sum求同一條重鏈上兩點間路徑權值和(可用線段樹維護)。

至此,樹上區間操作問題被完美解決。

附模板題:【bzoj1036】[zjoi2008]樹的統計count(具體**請參看本人其他部落格)

演算法入門 樹鏈剖分 輕重鏈剖分

目錄 3.0 求 lca 4.0 利用資料結構維護資訊 5.0 例題 參考資料 資料結構入門 線段樹 發表於 2019 11 28 20 39 dfkuaid 摘要 線段樹的基本 建樹 區間查詢 單點修改 及高階操作 區間修改 單點查詢 區間修改 區間查詢 標記下傳 標記永久化 閱讀全文 樹鏈剖分用...

樹鏈剖分 樹鏈剖分講解

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

演算法詳解 樹鏈剖分

1 給你乙個序列,再給你一堆詢問區間,對於每個詢問區間,請你求區間內的最大值 累加和等等。對於這個問題,我們是早就做爛的了,線段樹 樹狀陣列等資料結構都能輕鬆求,這裡不再詳述。2 給你一棵樹,再給你一堆詢問,每次給你兩個點,讓你求兩個點之間的路徑中的點權最大值 點權和等等。對於這個問題,我們很顯然不...