1. 程式人生 > 其它 >【筆記】樹上問題

【筆記】樹上問題

來自\(\texttt{SharpnessV}\)省選複習計劃中的樹上問題


樹上問題是\(\rm OI\)中必考的難點。


P5018 [NOIP2018 普及組] 對稱二叉樹

判斷帶點權有根二叉樹是否同構。

因為有根且是二叉樹,這極大簡化了判斷同構的過程。

我們定義函式 bool check(int x,int y) 判斷以 \(x/y\) 為根的子樹是否同構。

顯然兩顆樹同構,他們的子樹也要同構,我們遞迴下去即可,這樣我們就得到了 \(\rm O(N^2)\) 的演算法。

只有在兩顆子樹大小相同的時候才可能同構,子樹大小不相同直接剪枝即可。每個點被check時子樹大小必定翻倍,所以時間複雜度是 \(\rm O(N\log N)\)

Code


P2195 HXY造公園

支援加邊和查詢聯通塊直徑。

根據直徑的最長性質,我們加邊的時候一定是將兩棵子樹的直徑中點連起來。

所以我們並查集維護直徑長度即可。

Code


P1099 [NOIP2007 提高組] 樹網的核

求樹的直徑上的\(\le s\)的一段的偏心距的最小值。

我們可以將直徑上的點刪除,得到一個森林,然後對每棵計算最大深度。

那麼一段的最大偏心距一定是這一段到直徑兩段的距離,和所有森林的深度的最大值。

有人可能會問森林中有的子樹沒有接在這一段上啊,但是根據樹的直徑的性質,這些子樹的最大深度一定不大於這一段到直徑兩段的距離。因為如果大於,則樹的直徑可以更長。

樹的直徑的求法有兩種。

第一種是兩遍\(\rm DFS\),第一遍任取一個點找出距離最遠的點\(A\),第二遍找出距離\(A\)最遠的點\(B\)\(A-B\)就是直徑。不難用反正法證明不存在更長的路徑。

第二種方式是任取一個節點為根,然後計算每個節點向下的最大深度,然後列舉\(\rm LCA\)合併答案。

兩種方法各有優劣,第一種不能處理負邊權,第二種不能求出具體路徑。

Code


P3629 [APIO2010]巡邏

\(\rm k=1\)時,不難發現直接求出樹的直徑即可。

\(\rm k=2\)時,可以看出是兩個 \(k=1\) 的疊加。

如果構成一個環,那麼除了新加的邊,其他的邊都要走一遍。

那麼兩個環的重疊部分需要走兩遍。所以在求出樹的直徑後,將直徑上的邊權改為\(-1\)

,再求一次直徑就是第二次需要優化的路徑。

Code


P3938 斐波那契

思維題。

肉眼可見祖先節點編號小於子孫節點。

首先每天新增的節點數構成斐波那契數列,我們可以求出 \(a\) 是哪一天加入的。

而每一天新增節點編號是連續的一段,新增節點的父節點編號也是連續的一段,所以新增節點與父節點的編號差就是斐波那契數列的項。

Code


P4281 [AHOI2008]緊急集合 / 聚會

求三個點在樹上的重心。

顯然重心在任意兩點的路徑上。

我們先求出\(x-y\)的路徑,再求出\(z\)到路徑上的最小值。根據\(\rm LCA\)的高低討論一下即可。

Code


P5658 [CSP-S2019] 括號樹

定義狀態\(f[i]\)表示\(1-i\)路徑構成的串,以\(i\)結尾的合法括號串個數

顯然當\(i\)'('\(f[i]=0\)

\(i\)')'並且沒有相應的左括號與其匹配時,\(f[i]=0\)

設與')'匹配的左括號在節點\(j\),有\(f[i]=1+f[fa[j]]\)

我們只需要開一個全域性棧對括號進行匹配

對於每個\(si\)\(Ans_{si}=\sum_{j\text{在1-i路徑上}} f[j]\)

對此我們只需要記錄一個字首和即可

時間&空間複雜度\(\rm O(N)\)

Code


P2279 [HNOI2003]消防局的設立

本題做法較多,有\(\rm DP\),貪心等,時間複雜度從$\rm O(N^2) \(到\)\rm O(N) $都有。

這裡介紹最優的貪心演算法。

一個不難想到的貪心就是每次覆蓋最深的節點。因為最深的節點必須覆蓋,而不同的最深的節點覆蓋的先後順序沒有影響。

所以我們需要支援標記一個點,並支援查詢一個點距離\(2\)的範圍內是否有點被標記。

我們只用記錄每個節點是標記,子節點是否標記,孫子節點是否標記即可。

桶排可以做到線性時間複雜度。

Code


P5659 [CSP-S2019] 樹上的數

\(\rm CSP/NOIP\)最難的題。

這道題有兩種不同的思維路線,得到兩種不同的方法。

第一種:菊花圖。

觀察一下,發現刪完後所有數字構成一個單環\(a_1\to a_2\to \cdots\to a_n\to a_1\),每個箭頭表示數字的最終移動方向。

所以我們貪心移動,避擴音前成環即可,用並查集維護。

同理對於任意樹,把每個節點拆出來,與它相鄰的所有陣列最終也一定構成一個單環,對每個節點維護一個並查集即可。

時間複雜度\(\rm O(N^2\log N)\)

第二種:鏈

觀察一下,發現如果鏈上一點移動到另一點,則路徑上所有邊的先後順序是固定的。

同理對於任意樹,把每次移動路徑拿出來,則路徑上的邊的現後順序也是固定的,我們可以用連結串列維護這些邊的先後順序。

時間複雜度\(\rm O(N^2)\)

Code


P5666 [CSP-S2019] 樹的重心

首先一顆樹的重心最多隻有兩個。

每個點向重心移動方向是固定的,所以我們可以倍增優化。

考慮斷開鏈的影響,需要二次掃描換根。

Code


CF1486F Pairs of Paths

給定\(M\)個路徑,求相交恰好為一個點的路徑對數。

結論,兩個路徑如果相交則交一定過一個路徑的\(\rm LCA\),且是較深的\(\rm LCA\)

考慮分開算貢獻,分為\(\rm LCA\)相同時的貢獻,和\(\rm LCA\)不同時的貢獻。

我們對每條路徑按\(\rm LCA\)深度排序,對於第一種開桶計算貢獻,後者我們用樹狀陣列維護單點加子樹和即可。

Code


P6776 [NOI2020] 超現實樹

四種不同形態的子樹可以合成一個\(\rm End\)節點,所以我們分層遞迴下去分別判斷四種情況即可。

Code