【筆記】樹上問題
來自\(\texttt{SharpnessV}\)的省選複習計劃中的樹上問題。
樹上問題是\(\rm OI\)中必考的難點。
判斷帶點權有根二叉樹是否同構。
因為有根且是二叉樹,這極大簡化了判斷同構的過程。
我們定義函式 bool check(int x,int y)
判斷以 \(x/y\) 為根的子樹是否同構。
顯然兩顆樹同構,他們的子樹也要同構,我們遞迴下去即可,這樣我們就得到了 \(\rm O(N^2)\) 的演算法。
只有在兩顆子樹大小相同的時候才可能同構,子樹大小不相同直接剪枝即可。每個點被check
時子樹大小必定翻倍,所以時間複雜度是 \(\rm O(N\log N)\)
支援加邊和查詢聯通塊直徑。
根據直徑的最長性質,我們加邊的時候一定是將兩棵子樹的直徑中點連起來。
所以我們並查集維護直徑長度即可。
求樹的直徑上的\(\le s\)的一段的偏心距的最小值。
我們可以將直徑上的點刪除,得到一個森林,然後對每棵計算最大深度。
那麼一段的最大偏心距一定是這一段到直徑兩段的距離,和所有森林的深度的最大值。
有人可能會問森林中有的子樹沒有接在這一段上啊,但是根據樹的直徑的性質,這些子樹的最大深度一定不大於這一段到直徑兩段的距離。因為如果大於,則樹的直徑可以更長。
樹的直徑的求法有兩種。
第一種是兩遍\(\rm DFS\),第一遍任取一個點找出距離最遠的點\(A\),第二遍找出距離\(A\)最遠的點\(B\),\(A-B\)就是直徑。不難用反正法證明不存在更長的路徑。
第二種方式是任取一個節點為根,然後計算每個節點向下的最大深度,然後列舉\(\rm LCA\)合併答案。
兩種方法各有優劣,第一種不能處理負邊權,第二種不能求出具體路徑。
\(\rm k=1\)時,不難發現直接求出樹的直徑即可。
\(\rm k=2\)時,可以看出是兩個 \(k=1\) 的疊加。
如果構成一個環,那麼除了新加的邊,其他的邊都要走一遍。
那麼兩個環的重疊部分需要走兩遍。所以在求出樹的直徑後,將直徑上的邊權改為\(-1\)
思維題。
肉眼可見祖先節點編號小於子孫節點。
首先每天新增的節點數構成斐波那契數列,我們可以求出 \(a\) 是哪一天加入的。
而每一天新增節點編號是連續的一段,新增節點的父節點編號也是連續的一段,所以新增節點與父節點的編號差就是斐波那契數列的項。
求三個點在樹上的重心。
顯然重心在任意兩點的路徑上。
我們先求出\(x-y\)的路徑,再求出\(z\)到路徑上的最小值。根據\(\rm LCA\)的高低討論一下即可。
定義狀態\(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)\)
本題做法較多,有\(\rm DP\),貪心等,時間複雜度從$\rm O(N^2) \(到\)\rm O(N) $都有。
這裡介紹最優的貪心演算法。
一個不難想到的貪心就是每次覆蓋最深的節點。因為最深的節點必須覆蓋,而不同的最深的節點覆蓋的先後順序沒有影響。
所以我們需要支援標記一個點,並支援查詢一個點距離\(2\)的範圍內是否有點被標記。
我們只用記錄每個節點是標記,子節點是否標記,孫子節點是否標記即可。
桶排可以做到線性時間複雜度。
\(\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)\)。
首先一顆樹的重心最多隻有兩個。
每個點向重心移動方向是固定的,所以我們可以倍增優化。
考慮斷開鏈的影響,需要二次掃描換根。
給定\(M\)個路徑,求相交恰好為一個點的路徑對數。
結論,兩個路徑如果相交則交一定過一個路徑的\(\rm LCA\),且是較深的\(\rm LCA\)。
考慮分開算貢獻,分為\(\rm LCA\)相同時的貢獻,和\(\rm LCA\)不同時的貢獻。
我們對每條路徑按\(\rm LCA\)深度排序,對於第一種開桶計算貢獻,後者我們用樹狀陣列維護單點加子樹和即可。
四種不同形態的子樹可以合成一個\(\rm End\)節點,所以我們分層遞迴下去分別判斷四種情況即可。