樹就是一個無向無環聯通圖。
要注意若沒有規定,任何節點都可以成為根,此時樹為無根樹。
最常用的是鄰接陣列(adjacency list)
遞迴或 stack,用來計算子樹資訊、深度等:
用 queue,適合計算最短路或節點距離:
以上計算出所有節點相對於 start
的距離。
給定每個人的上司,計算出每個人的下屬有幾個。
將所有人的上司 – 下屬關係用一條邊連著,可以構成一顆樹。用 DFS 遞迴在樹上 DP:
上面程式計算出每個節點的子樹大小(cnt[u]
代表節點 的子樹大小)。
最後再把每個人的子樹大小減去一就是下屬的數量。
找出樹直徑。
樹上任意兩點之間的最長簡單路徑長稱為樹的直徑(可能有多條相同長度的路徑),可以用兩次 DFS || 樹 DP 在 時間內求出。
先隨便找出一個點 ,找出距離點 最遠的點 ,再找距離點 最遠的點 。則 為樹直徑。證明
隨便選擇一個點作為根,計算出每一個點往下的最大深度與次大深度,合併之後為通過當前點的最大路徑,將所有點取最大值即為樹直徑。
找出對於每個節點最遠點的距離。
對於每一個節點,可以發現離他最遠的點一定是往他的子節點們或是父節點其中一條路徑之內。
一樣用 DFS 遞迴下去處理,對於每一個節點 ,記錄往子節點 path[u]
可以達成最長長度 maxDist[u]
,和第二長的長度 secMax[u]
。
接著重頭開始跑另外一次遞迴,如果當前節點 的 maxDist[u]
不是往欲處理節點 ,則可以得知 maxDist[v]
一定小於等於 maxDist[u] + 1
(否則在計算 maxDist[u]
時會選擇 maxDist[v] + 1
)。
而如果 maxDist[u]
是往 ,檢查是否要更新 maxDist[v]
或 secMax[v]
成 secMax[u] + 1
。
最後處理完,maxDist[k]
即為節點 到任何節點最遠距離。
對於每一個節點,輸出他到其他所有點的距離和。
我們可以在 時間內找到一個點到所有其他點的距離和,但如果要計算所有的點, 會炸。
其實可以發現如果計算出了一個人的答案,則他的子節點答案可以 時間內用父節點求出。
記錄 sz[v]
為節點 的子樹大小,若要將當前節點 往子節點 推答案,所有在 之上的點相對於 的距離會加一,而相對於所有 以下的則會加一,列出轉移式 ,就能在 時間得出所有點的答案。
給定每個人的從屬關係和 個詢問: 上面 層的的上司是誰?
如果每次查詢 總複雜度 會炸掉。
可以利用之前 Sparse Table 類似的思想,記錄每一個人往上 層的上司是誰,就可以在 時間內建表, 時間內查詢。
定義 succ[k][u]
為節點 往上走 的人,可以得到轉移式 ,即為往上走 再往上走 ,等同於 。
查詢時也只需要把 拆分成 的冪次相加,往上跳到答案即可。
給定每個人的從屬關係和 個詢問:距離 和 最近的共通祖先為何?
求最低共通祖先(Lowest Common Ancestor)通常有兩種寫法:倍增法和歐拉迴路。
用剛剛講到的 Binary Lifting,先建好表和計算深度,給定兩個節點 和 後,先把比較深的節點提高,讓兩節點深度一樣,可以用 x = succ[lg(depth[x] - depth[y])][x]
達成( 的情況,若反過來可以 swap 就好)。
接著只需要一直往上直到同個節點就好,為了實作方便可以先定位到 LCA 下面一格再往上就好。
核心思想是做一次 DFS,記錄每個點進出的時間,之後會變成一個一維的時間線,兩個點之中的某個時間點,其深度最小就等同於兩點的 LCA。記錄 timer[cnt]
是進/出時間為 cnt
的點,id[u]
為節點 進入的時間點。處理完之後可以在 時間內建好 Sparse Table,就可以在 時間查詢 LCA。
像歐拉迴路這種把樹「壓平」成一維的操作就是樹壓平。LCA 還有其他各種方式可以求,有一種更簡單的樹壓平求 LCA 方式可以參考這篇文章。
很多題目都會需要求出 LCA,例如樹上兩點求最小距離:CSES - Distance Queries
至於為什麼要壓平,當然是套資料結構(剛剛求 LCA 時是套 Sparse Table),根據題目也可以套 BIT、線段樹之類的。
題目:CSES - Subtree Queries
給定一顆樹,有兩種操作:將節點 的值修改成 / 查詢節點 的子樹值之和。
首先,這看起來就很 BIT,於是我們就把它壓平,記錄 st[u]
為節點 在 DFS 下出現的時間,en[u]
則為離開的時間。肉眼可見的發現了他的子樹時間序就全部在 st[u]
和 en[u]
之間了,把 個時間點丟進 BIT 裡面(結束的時間點可以重複,所以只需要 個時間點),就可以在 時間內改值跟查詢了。
壓平後也可以對樹上的一條鏈做查詢(如 SUM),入時間戳設正值出時間戳設負值。
下面的都是好題,還有 CSES 的 Tree Algorithm,樹跟 DP 一樣就是要多刷題,盡量寫,不懂的可以問。
P2195 HXY造公园
P5536 【XR-3】核心城市
P1099 NOIP 2007 提高组 树网的核
P1352 没有上司的舞会
P1040 NOIP 2003 提高组 加分二叉树
P1122 最大子树和
P1273 有线电视网
P2014 CTSC1997 选课