虛樹-學習筆記
簡介
虛樹,是一種處理樹上動態規劃的資料結構。但他適用於這種情況:多組詢問,每次詢問一個點集,求這些點集的答案。我們當然可以每次都對整一棵樹跑一遍,但如果有 \(1e5\) 個詢問呢?這時候別的演算法很難處理,於是虛樹就誕生了。
如果題目沒有多組詢問的時候可以用樹上 DP 做,那麼就可以考慮虛樹了。
演算法
我們設每次詢問的點集為關鍵點,點選大小為 \(k\)。
虛樹處理點集的方法比較特殊。對於一道題目,如果我們發現每組詢問只會涉及到詢問的點集和他們兩兩之間的 LCA,那麼就可以用虛樹來做。
對於每組詢問,我們可以線上處理。我們相當於只是把所有需要用到的點處理出來,然後按照原樹的祖先後代關係重新建一棵虛樹
建樹的話,我們需要每個點,以及他們兩兩之間的LCA。可以證明,每次總的點數不會超過 \(2k\) 。因此,虛樹演算法的時空複雜度是 \(\text{O}(\sum k)\),滿足我們的需求。
有一個經典的建虛樹方法,可以做到 \(\text O(k)\) 複雜度。就是先把所有關鍵點按照他們原樹上的 dfn 序排序,然後一個點一個點插入。具體的話,用單調棧維護當前虛樹上的一條鏈,然後插入一個點和他與鏈上所有點的 LCA。具體的話就 懶得寫了,就是分類討論一下,一共有四種情況,分別是:
- 這個點是當前這條鏈的低端。那麼直接插入鏈尾。
- 這個點與原鏈尾的 LCA 是倒數第二個點。此時彈出棧頂,插入這個點。
- 這個點與原鏈尾的 LCA 在倒數第二個點下方。此時彈出棧頂,插入 LCA,插入當前點。
- 這個點與原鏈尾的 LCA 在倒數第二個點上方。此時彈出棧頂,並迴圈,直到不是當前情況。
這裡可以這麼做是因為當前點和鏈上的點的 LCA 最多隻會新建一個點。當然這裡只是理清一下自己的思路,如果看不懂可以去看別的部落格。
然後建完樹跑 DP 就好了。當然有些題難點在於虛樹而不在 DP,還是要視實際來取捨。
注意點:
- 每次清空虛樹只需要把當前樹上的邊清空就好了。記得清空不是關鍵點但是是 LCA 的點!
- 每次 DP 記得初始化陣列。
- 為了方便,我們一般都是先把 \(1\) 號點插入虛樹。這樣可以避免討論一些邊界情況而不會影響答案。當然還是要具體題目具體討論。
那麼虛樹就說完了。
例題
比較模板了。注意建出虛樹後兩點之間的權值要用倍增或者樹剖求出原樹上兩點之間邊權最小值。樹剖的話就是先是跳 top,然後在同一條鏈比較麻煩,但應該也是可以做的。就是對於每一條鏈的鏈底開一個 vector,儲存這個點往上跳 k 步的最小值,這樣複雜度也是對的。但我沒打過 因為要用 vector,可能常數略大。
大碼量題。主要是 DP 的細節。需要儲存每個點為根的子樹中,每個關鍵點到自己的距離和,距離最大值,距離最小值,以及每個兒子為子樹的距離最大值的最大值和次大值,最小值同理。但也算比較套路了,做題多了就可以自己推出來。
算是比較思維吧。也是 DP 比較不好想,需要一番思考。
這兩道題我自己也沒做,留個坑以後來填。
那麼虛樹這一章就完滿結束了。