從 dfs 序求 lca 到虛樹到樹分塊 學習筆記

Fun_Strawberry發表於2024-07-02

前言

想象我在口胡三樣我都不熟悉的東西並嘗試稱之為“學習筆記”。

其實不過是我自己對於它的一點小理解,甚至可能是錯誤的!

無所謂,口胡!口胡!口胡!口胡!口胡!

一些備註

\(dfn_u\) 為點 \(u\) 的 dfn 序,\(nfd_i\) 表示第 \(i\) 個 dfs 到的點是啥(前者的反陣列)

dfs 序求 lca

這個很簡單,想象把點按照 dfs 序重排後求 lca,設兩點為 \(u≠v\),則 lca 為:

它們 dfs 序區間內深度最小的點的父親。

這個 ST 表搞一下即可 \(O(1)\) 查詢 lca!

證明:由於 \(u,v\) 必然分別在 lca 的兩個子樹中間,想象過程中必然存在一個跳越子樹的過程,跳到新的子樹的根,其父親就是 lca。

(想象這裡有圖片)

相應的也有這樣的結論:

\(u,v\) 間(dfs 序上)所有的相鄰點對的 lca,深度或者 dfs 序最小的就是了。

證明同理,想象中間跳子樹的過程。

後面這個沒啥直接用處但是等會要用。

假的,它告訴我們:dfn 序上 lca 和 min 的性質較相似。

虛樹

想象一類樹形問題,每次只取出樹上一小部分點進行詢問,詢問次數和點總和皆是 \(O(n)\)

此時不能對整棵樹處理,只能取出他們中的一部分點建樹,這就是虛樹了。

但是隻用給出的 \(k\) 點建樹是不夠的,下圖這樣的資訊就儲存不下來了:

(想象這裡有圖片)

那麼我們還應該保證它們的所有 lca 都在虛樹上。

然後根據前面結論 2,你只需要取出 dfn 上所有相鄰點對的 lca(共 \(k-1\) 個),加上一個根節點,共 \(2k\) 個點就行了。

然後具體建邊方法很多,我比較喜歡按尤拉序排序的方式。

這個後面再來補充一下!

樹分塊

雖然說,樹分塊的建樹過程模板是 P2325 [SCOI2005] 王室聯邦,但是我從這個角度切入樹分塊就糊塗了,可能是我太菜!

其實樹分塊的理解方式是:

選出一些點 \(a\),建一棵虛樹,並把虛樹上所有點視為 關鍵點,此時我們發現,任意一條關鍵點上的邊,對應著原來樹上這兩個點之間的路徑和一些小枝枝(伸不出去的)。

這棵樹被叫做原樹的伸縮樹

那麼此時就發現一條原樹上的路徑變成了現在三個部分:

\(u\) 到某個關鍵點 + 伸縮樹上兩個關鍵點之間的路徑 + 關鍵點二 到 v 的路徑

那麼這看起來很像分塊“大段維護,區域性樸素”的思想,所以我們只需要讓伸縮樹上每條邊對應的原樹邊集儘量平衡就行了。

一般來說平衡應該是 \(B=\sqrt{n}\),如何達到這個平衡呢?

講兩個方法!

隨機撒點

是的!隨機撒 \(O(B)\) 個點建出虛樹(當然他們的 lca 也是要包含的),對於大多數問題來說,只包含對於鏈的詢問,不需要特別的性質(比如板子題就不需要了),這個夠用了。

Top Cluster

想象回到 P2325 [SCOI2005] 王室聯邦,現在來講一下這道題目的做法:

dfs 保證每個子樹只剩下殘餘的 \(<B\) 個點,那麼簡單合併,每當超過了 \(B\) 個就把他們視作一個新的塊,最後必然剩下 \(<B\) 個點視作新的殘餘子樹,遞迴上去解決問題。

就搞定了。

但是直接這樣發現每個塊有不止兩個向外連的節點,並不符合樹分塊的伸縮樹性質。

有兩個解決方案:第一個,把這個塊建出虛樹,虛樹上每一條邊都改成一個新塊,就行了,現在每個塊只有 \(2\) 個關鍵點了。

第二個,直接分的時候,如果加入之後產生了第三個關鍵點,就不加了,直接把當前的點成一塊。

這兩個做法其實差不多的,而且可以證明這樣分是平衡的,但是我不會證明。

後記

圖還沒搞,先發布吧。

相關文章