樹鏈剖分,簡單的說,其實就是把一棵樹拆成很多條鏈,然後變成乙個序列,這樣就可以把樹上的問題轉換成簡單的區間問題,而區間問題我們可以用線段樹和樹狀陣列等等資料結構來維護。
首先是一些必須知道的概念:
重結點:子樹結點數目最多的結點;
輕節點:父親節點中除了重結點以外的結點;
重邊:父親結點和重結點連成的邊;
輕邊:父親節點和輕節點連成的邊;
重鏈:由多條重邊連線而成的路徑;
輕鏈:由多條輕邊連線而成的路徑;
其實,dfs序列就是一種樹鏈剖分,因為dfs序列的連續一段在樹上可以是一條鏈。不過dfs序完全是亂來的,樹鏈剖分的話我們會選擇節點最多的子樹進行優先遍歷(即輕重邊剖分),這樣可以保證效率。
演算法中定義了以下的陣列用來儲存上邊提到的概念:
ct[u]:儲存以u為根的子樹節點個數
top[u]:儲存當前節點所在鏈的頂端節點
dep[u]:儲存節點u的深度
fa[u]:儲存節點u的父親節點
l[u]:儲存樹中每個節點u剖分以後的新編號(dfs的執行順序)
r[u]:儲存以每個節點為根節點的子樹剖分以後的區間的右端點編好(dfs的執行順序)
g[u]:儲存當前節點在樹中的位置
樹鏈剖分能完成樹上的修改和詢問操作:
求lca
修改/求 樹上某 節點/邊權 及其子樹上所有節點的(最)值;
修改/求 樹上某兩點路徑間的 節點/邊權 的(最)值;
void dfs(int x, int f)
} void dfs(int x, int t)
r[x] = cnt;
}
上述的**,就是兩次dfs搞出乙個dfs序列,這個序列就是樹鏈剖分的結果了,對不同的題目,可以根據自己的需要進行部分修改。而這樣搞定之後,[l[x],r[x]]就是x為根的子樹,從l[x]到l[top[x]]就是從x開始到達重鏈的頂端。
剩下的就交給資料結構維護了。
而修改和查詢操作原理是類似的,以查詢操作為例,其實就是個lca,不過這裡使用了top來進行加速,因為top可以直接跳轉到該重鏈的起始結點,輕鏈沒有起始結點之說,他們的top就是自己。需要注意的是,每次迴圈只能跳一次,並且讓結點深的那個來跳到top的位置,避免兩個一起跳從而插肩而過。
int getans(int x, int y)//查詢節點x到節點y路徑上點的最大值
if (dep[x] > dep[y]) swap(x, y);
if (x == y) return ans;
return max(ans, query(1, 1, n, g[x] + 1, g[y]));
}
樹鏈剖分小結
寫在前面 樹鏈剖分是一種高效的資料結構,它的作用是將一棵點權樹 一定要是點權樹 拆分成鏈,然後用各種方法來高效解決問題。樹鏈剖分就是以一種方式遍歷樹,將原來的樹該成一條鏈,然後對這條鏈進行各種高效的操作。這種方式就是優先遍歷最大的子樹,我們還是嚴格的從定義學起。定義 重兒子 siz u 為v的子節點...
樹鏈剖分 樹鏈剖分講解
好了,這樣我們就成功解決了對樹上修改查詢邊權或點的問題。下面放上 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...
演算法入門 樹鏈剖分 輕重鏈剖分
目錄 3.0 求 lca 4.0 利用資料結構維護資訊 5.0 例題 參考資料 資料結構入門 線段樹 發表於 2019 11 28 20 39 dfkuaid 摘要 線段樹的基本 建樹 區間查詢 單點修改 及高階操作 區間修改 單點查詢 區間修改 區間查詢 標記下傳 標記永久化 閱讀全文 樹鏈剖分用...