樹上技巧

HLD

heavy-light decomposition

定義

給每條邊分成輕,重兩類

滿足每個非葉子節點恰好有一條往下的重邊,並且重邊是最大的那個子樹

如果把輕邊拔掉的話樹會被分成很多條鏈,這就是我們想要討論的部分

觀察會發現,考慮任何一條只往上走路徑,路徑上最多會經過 O(log n) 條輕邊

證明跟啟發式合併一樣

例題

如果我們對於每條鏈開一棵線段樹,那麼

單點修改就單點修改

路徑查詢可以拆成 O(log n) 條路徑,每條考慮的路徑都是區間查詢

=> O(log n) 次線段樹上查詢 => O(log^2 n)

 

那就 done 了

HLD 主要用在要算路徑

樹壓平主要用在算子樹

 

所以不要看到樹上改值就直接砸 HLD 了

我覺得我上次寫 HLD 已經是一年多以前了

重心剖分

有人叫他 cd,有人叫他 dc,請投票

還記得重心吧

重心分治分為三步驟:

  1. 找到重心
  2. 對重心算答案
  3. 把重心拔掉並分治到每一棵子樹下去

例題:JOISC 2020 D4 A 

有一棵樹,每個點都有一個顏色,你要選出一個顏色的子集 S 滿足

  1. 對於所有滿足顏色皆屬於 S 的點對,他們之間的簡單路徑上的所有點的顏色都屬於 S
  2. S 是可能的集合裡面最小的

考慮定根,假設我們選出的 S 一定要包含根
那麼你可以導出一個 greedy algorithm

let V = {與根有相同顏色的點的集合}

每次任意選出一個 V 中的元素,並把他到根的路徑上面的所有顏色都加到 S 裡面,並且把所有還沒看過的並且顏色在 S 裡面的點都加進 V 裡面,直到 V 為空

 

不難驗證現在的 S 是定根下的最佳解

 

上面的工作可以用一個 queue 完成

因此考慮重剖,依次考慮過 S 必須包含每個點的情形取最小值就是答案了

 

來算時間複雜度

根據重心的規則,每次拔掉重心剩下的子樹的大小要 <= n/2

代表只能拔掉 O(log n) 次重心就會退化成一個點

定根在重心的複雜度是 O(n)

一個點只會出現在 O(log n) 棵樹裡面

=> 總複雜度 O(n log n)

算是 JOISC 裡面少數簡單的題目了

題外話是這場的 B 跟 C 也都很有趣,推薦寫

DSU on tree

算是一種 HLD 的應用

直接上步驟:

  1. 把輕子樹的答案都算好,然後丟掉
  2. 把重子樹的答案算好,不要丟掉
  3. 再遍歷一次輕子樹,並且把答案跟重子樹的答案合併然後得到當前節點的答案

時間一樣一個 log,好處是因為可以丟掉資訊通常可以省下空間或是大結構的數量

其他酷東西

啟發式消滅,可以直接去看題解

Made with Slides.com