1. 程式人生 > 其它 >虛樹學習筆記

虛樹學習筆記

\[\huge \rm 虛樹 \]
\[\Large\rm 演算法簡介 \]

\(\quad\)在一些問題中,我們只關心一些關鍵點的資訊,同時我們需要維護他們之間的樹形結構,於是可以想到將它們和它們之間的 \(\rm LCA\) 拉出來建一顆樹,這棵樹就被稱作虛樹。

\(\quad\)有一個樸素的想法,就是對它們兩兩求 \(\rm LCA\),並且對求出的 \(\rm LCA\) 再求 \(\rm LCA\),中途判重,最後按照祖先關係連邊。

\(\quad\)經過觀察可以發現,我們只需要按照 \(\rm dfs\) 虛從小到大的順序列舉每個點,將相鄰兩點求 \(\rm LCA\) 即可。具體來說,我們可以維護一個棧,這個棧中存的是當前點在虛樹上的所有祖先。設當前棧頂為 \(v\)

,對於新加入的結點 \(u\),我們對它進行分類討論 :

  • \(\rm LCA(u,v)=v\),那麼直接將 \(u\) 入棧。
  • \(\rm LCA(u,v)\) 是棧中某個不為 \(v\) 的點,則彈棧直到 \(\rm LCA(u,v)\),每彈一個就向棧中比它深度多 \(1\) 的結點連邊,最後並把 \(u\) 入棧。
  • \(\rm LCA(u,v)\) 不在棧中,那麼彈棧直到棧頂的 \(\rm dfs\) 序小於 \(\rm LCA(u,v)\)\(\rm dfs\) 序,每彈一個就向棧中比它深度多 \(1\) 的結點連邊,並把最後一個彈出的結點向 \(\rm LCA(u,v)\)
    連邊,最後把 \(\rm LCA(u,v)\)\(u\) 入棧。

\(\quad\)不難發現這樣一定是對的,並且設關鍵點數量為 \(k\),則虛樹內點數不超過 \(2k.\)

\(\quad\)在一棵有 \(n\) 個點的樹上建立一顆有 \(k\) 個點的虛樹的時間複雜度為 \(\Theta(k\log n).\)


\[\Large \rm 習題 \]

\(\large \rm [SDOI2011]消耗戰\)

\(\quad\)首先有一個暴力 \(\rm DP\),設 \(dp_u\) 表示將 \(u\) 的子樹內所有關鍵點與 \(u\) 的聯絡斷開所需要的最小代價。轉移時列舉 \(u\)

的所有子節點,分類討論 :

  • \(v\) 為關鍵點,那麼必須切斷這條邊,有 \(dp_u=dp_u+w_{u,v}\)
  • \(v\) 不為關鍵點,那麼可以切斷這條邊也可以從 \(dp_v\) 轉移,故有 \(dp_u=dp_u+min\{dp_v,w_{u,v}\}\)

\(\quad\)最終答案就是 \(dp_1\),這個暴力的時間複雜度是 \(\Theta(qn).\)

\(\quad\)觀察到 \(\sum k\leqslant 5e5\),所以考慮對每次詢問建虛樹。

\(\quad\)建虛樹之前需要記 \(M_u\) 表示從 \(1\)\(u\) 的路徑上最短的邊權,因為只需要切掉最短的邊權就可以讓點 \(u\)\(1\) 不連通,而切上面的邊是更優的,所以可以用 \(M_u\) 直接替換轉移方程中的 \(w_{u,v}.\)

\(\quad\)時間複雜度 \(\Theta(\sum k\log n).\)

\(\large \rm [HEOI2014]大工程\)

\(\quad\)首先建虛樹。

  • 對於兩兩之間距離之和,可以計算每條邊的貢獻。
  • 對於最大\(/\)最小值,可以樹形 \(\rm DP\) 或點分治。

\(\quad\)時間複雜度 \(\Theta(\sum k\log n).\)

\(\large\rm [SDOI2015]尋寶遊戲\)

\(\quad\)我們發現如果將虛樹建出來,那麼答案就是虛樹上所有邊權之和的兩倍。

\(\quad\)於是我們考慮動態維護虛樹的邊權之和。先把所有點的 \(\rm dfs\) 序求出來,然後我們發現邊權之和等於 \(\rm dfs\) 序相鄰兩點的距離之和加上最左邊和最右邊兩點的距離,於是我們可以使用 \(\rm set\) 維護,樹剖求 \(\rm LCA.\)

\(\quad\)時間複雜度 \(\rm \Theta(n\log n).\)