對於解決一類樹上的問題,可以將樹上問題轉化為區間上問題求解。樹鏈剖分是指將
樹剖成鏈以解決一些樹上難以解決的問題。
那麼,問題來了,應該按照怎樣的規則將樹剖分?
本文中的樹鏈剖分按照"重鏈"進行剖分。
首先要了解幾個概念:
對於乙個節點,定義:
重兒子:該節點的所有子節點中,包含子節點節點數目最大的節點(乙個節點最多乙個)樹鏈剖分一種剖分方式是重鏈剖分,使重鏈上的節點在重新編號後成為連續的一段區間,以方便維護。輕兒子:該節點除重兒子以外的子節點
重邊:連線該節點與重兒子的邊
輕邊:連線該節點與輕兒子的邊
重鏈:由重邊相接組成的鏈
輕鏈:由輕邊相接組成的鏈
按照"重鏈"剖分其實是一種啟發式剖分。此外,還有按照"長鏈"剖分。
援引巨佬部落格中的內容:
樹鏈剖分目的:樹路徑資訊維護。將一顆樹劃分成若干條鏈,用資料結構去維護每條鏈,複雜度\(\theta(n\log n)\)
劃輕重鏈目的:為了讓鏈上節點的編號連續。第一步,遍歷整棵樹,將每個節點,每條邊的資訊處理出來。我們在用線段樹維護鏈的時候,如果節點編號不連續,那麼就無法用線段樹。
需要處理的資訊有:
\(f[x]\):記錄節點\(x\)的父節點對於前四個資訊,很容易維護,只需要從根節點遞迴遍歷整棵樹即可。**如下:\(siz[x]\):記錄以節點\(x\)根的子樹大小
\(son[x]\):記錄節點\(x\)的重兒子
\(d[x]\):記錄節點\(x\)的深度
\(top[x]\):記錄\(x\)所在的重鏈的頂端節點(即\(x\)所在重鏈上深度最淺的節點。如果\(x\)不在重鏈上,那麼\(top[x] = x\))
\(id[x] = y\):記錄節點\(x\)的新編號\(y\)
\(rak[y] = x\):記錄新編號\(y\)對應的節點\(x\)
void dfs1(int x, int fa, int deep)
return ;
}
對於後面兩組資訊,我們需要解決的問題是,如何保證重鏈上的節點有連續的一段編號?
聯想到\(dfs\)序的性質,只要每次遞迴遍歷的時候優先遍歷重兒子,給其編號,就能保證重鏈上的節點有連續的一段編號了。
對於輕兒子,我們可以將其看作只有乙個節點的重鏈。**如下:
void dfs2(int x, int t)
return ;
}
至此,整棵樹剖分已完畢。我們得到了乙個序列,不難**這個序列的性質。
由於我們是按照求\(dfs\)序的方法對節點編號,因此,這個序列具有\(dfs\)序的特點。
特點一:對於一棵子樹,它所有節點的編號是連續的。同時,由於我們優先為重兒子編號,該序列還具有的特點是:
特點二:對於一條重鏈,它所有節點的編號是連續的。眾所周知,\(lca\)可以使用倍增求解。時間複雜是預處理\(\theta(n\log n)\)加上每次詢問\(\theta(\log n)\)。
如果使用樹鏈剖分求解\(lca\),可以更高效。那麼,如何運用呢?
\(lca\)的倍增方法是將兩個節點根據預處理出來的陣列不斷地向上跳。
樹鏈剖分也預處理了乙個指向\(x\)祖先的陣列\(top[x]\),是否可以仿照\(lca\)的模式,不斷向上跳呢?
答案顯然是可以的。
如果\(x\),\(y\)一直向上走,肯定會走到乙個公共節點。為了避免\(x,y\)"擦肩而過",每次我們只讓其中乙個向上走。
怎麼走呢?如果\(x,y\)不在一條重鏈上,那麼就讓深度較深的節點走到該條重鏈頂端的父節點,交錯著走,直到\(x,y\)在一條重鏈上。
如果\(x,y\)在一條重鏈上,那麼它們的\(lca\)顯然是它們兩個中深度較淺的節點。
可以證明,剖分出來的路徑不超過\(\theta(\log n)\)條,因此,樹鏈剖分求\(lca\)的時間複雜度是:預處理\(\theta(n)\)加上查詢\(\theta(\log n)\)
設這條路徑的兩個端點分別為\(x,y\)。由樹的性質可以知道,\(x,y\)之間只存在唯一的一條路徑。這條路徑是\(x\to lca(x,y)\to y\)。
因此,我們只需要在求\(lca(x,y)\)的同時,將經過的所有重鏈進行維護即可。
假設用線段樹進行區間維護,那麼時間複雜度為:
線段樹區間修改複雜度\(\theta(\log n)\),樹剖求\(lca(x,y)\)時間複雜度\(\theta(\log n)\),總時間複雜度\(\theta(\log^2n)\)
留坑待補...
樹鏈剖分總結
樹鏈剖分思想不是很複雜。首先給出幾個定義吧 其核心思想就是,將一棵樹拆成多條鏈,然後對於每一條鏈,就用資料結構去維護。有個不會證明的性質,就是如果將一顆樹拆成多條重鏈和輕邊,那麼重鏈的個數不會超過 log 2n 輕邊的邊數也不會超過 log 2n 因為這個性質,很多操作我們可以很高效地完成。這個之後...
樹鏈剖分總結
把樹剖成鏈再操作 部落格安利 維護7個不同陣列,通常和線段樹一起使用 1 模板題 p3384 模板 樹鏈剖分 p3178 haoi2015 樹上操作 p2590 zjoi2008 樹的統計 p2146 軟體包管理器 p2420 讓我們異或吧 2 應用題 p3950 部落衝突 p3038 牧草種植 1...
樹鏈剖分學習
樹鏈剖分 看了學習了樹鏈剖分 適用於在樹上的路徑操作。關鍵在於重鏈的構造,把它表示到了資料結構上的連續區間,降低了複雜度。主要操作步驟實際上有三個部分 1 構造重鏈 2 如何維護資料 3 分解為輕重鏈的查詢與修改 include include include include define n 10...