diff --git a/src/graph/centroid.md b/src/graph/centroid.md index 47b3b38c..46402a8c 100644 --- a/src/graph/centroid.md +++ b/src/graph/centroid.md @@ -48,15 +48,15 @@ ### 如何求重心? -通常我們要先求出子樹大小才能找重心,所以我們可以任選一個點當根,先求出每個節點子樹大小。接著開始從根開始遞迴檢查,遍歷所有子節點,如果有一個子節點子樹大小 \\( > \frac{N}{2} \\), 則重心一定在這個個子樹裡,所以就往這個子節點遞迴。反之如果所有子節點子樹大小都不超過一半,則現在所在的點即為重心。 +通常我們要先求出子樹大小才能找重心,所以我們可以任選一個點當根,先求出每個節點子樹大小。接著開始從根開始遞迴檢查,遍歷所有子節點,如果有一個子節點子樹大小 \\( > \frac{n}{2} \\), 則重心一定在這個個子樹裡,所以就往這個子節點遞迴。反之如果所有子節點子樹大小都不超過一半,則現在所在的點即為重心。 另一種實作方式是一樣是任選一個點當根,先算出所有子樹大小,再透過整棵樹大小減去每個節點的子樹大小的方式,算出每個點父親節點所形成的子樹大小來判斷。 > [CSES Finding a Centroid](https://cses.fi/problemset/task/2079) > -> 給一棵 \\(N\\) 個節點的樹,輸出樹的重心編號 +> 給一棵 \\(n\\) 個節點的樹,輸出樹的重心編號 > -> - \\( 1 \le N \le 2 \times 10 ^ 5 \\) +> - \\( 1 \le n \le 2 \times 10 ^ 5 \\)
Sample Code @@ -108,10 +108,11 @@ int main() { > [BOI Village (Maximum)](https://codeforces.com/contest/1387/problem/B2) > -> 有一座 \\(N\\) 個點的樹形成鎮,每一個點住著一位居民,編號從 \\( 1 \sim N \\)。 -> 有一天,居民們突然想要搬家,每個居民都要搬到不同的節點,但一個節點只能住一位居民,一個居民的搬家距離為舊家到新家的路徑上的邊數。> 請你給出一種搬家方案使所有居民的搬家距離總和最大。 +> 有一座 \\(n\\) 個點的樹形成鎮,每一個點住著一位居民,編號從 \\( 1 \sim n \\)。 +> 有一天,居民們突然想要搬家,每個居民都要搬到不同的節點,但一個節點只能住一位居民,一個居民的搬家距離為舊家到新家的路徑上的邊數。 +> 請你給出一種搬家方案使所有居民的搬家距離總和最大。 > -> - \\(1 \le N \le 10^5 \\) +> - \\(1 \le n \le 10^5 \\) 對於每一條邊 \\( (u, v) \\),將這條邊斷開後會把樹分成兩棵大小分別為 \\( SZ_u, SZ_v \\)的樹,所以這條邊至多對答案貢獻 \\( 2 \times \min(SZ_u, SZ_v) \\) (乘以 \\(2\\) 是因為兩個方向都最多被經過 \\( \min(SZ_u, SZ_v) \\) 次)。所以考慮每條邊的貢獻,答案最大會是 \\(\begin{equation*} \sum\limits_{(u, v) \in E} \min \\{SZ_u, SZ_v\\} \end{equation*}\\)。 @@ -120,15 +121,17 @@ int main() { 所以我們考慮以重心當根,則根節點的所有子樹大小都會 \\( < \frac{n}{2} \\),有很多種方式可以構造出解答,這邊講一個實作起來最簡單的。 -我們將以重心為根的樹先進行一次 DFS,並將依照每個點的拜訪順序紀錄成一個陣列 \\( a \\),將 \\(a\\) 向右循環平移 \\( \lfloor\frac{n}{2}\rfloor \\)得到新的陣列 \\(b\\),接著 \\( \forall{i \in 1 \sim N} \\),將 \\(a_i\\) 搬到 \\(b_i\\),這樣就得到一組解了。 +我們將以重心為根的樹先進行一次 DFS,並將依照每個點的拜訪順序紀錄成一個陣列 \\( a \\),將 \\(a\\) 向右循環平移 \\( \lfloor\frac{n}{2}\rfloor \\)得到新的陣列 \\(b\\),接著 \\( \forall{i \in 1 \sim n} \\),將 \\(a_i\\) 搬到 \\(b_i\\),這樣就得到一組解了。 ## Exercises > [CF 1406C Link Cut Centroids](https://codeforces.com/contest/1406/problem/C) > -> 給你一棵 \\(N\\) 節點的樹,你能刪除樹上的一條邊並且再加入一條邊,使得圖還是一棵樹。> 請問你有沒有辦法讓操作完的樹重心唯一? 請輸出一組可行的操作。 +> 給你一棵 \\(n\\) 節點的樹,你能刪除樹上的一條邊並且再加入一條邊,使得圖還是一棵樹。 > -> - \\(1 \le N \le 10^5\\) +> 請問你有沒有辦法讓操作完的樹重心唯一? 請輸出一組可行的操作。 +> +> - \\(1 \le n \le 10^5\\)
Hint @@ -138,13 +141,13 @@ int main() { > [CF 685B Kay and Snowflake](https://codeforces.com/problemset/problem/685/B) > -> 給你一棵 \\(N\\) 節點的有根樹,求每個子樹的重心。 +> 給你一棵 \\(n\\) 節點的有根樹,求每個子樹的重心。 > -> - \\(1 \le N \le 3 \times 10^5\\) +> - \\(1 \le n \le 3 \times 10^5\\)
Hint -在上面找重心的程式碼中,我們每次只會往子樹大小 \\( > \frac{N}{2} \\) 的節點走。 +在上面找重心的程式碼中,我們每次只會往子樹大小 \\( > \frac{n}{2} \\) 的節點走。 那我們試著將這個做法反過來,從葉子節點開始,假設現在所在節點為 \\(u\\),\\(u\\) 的父節點為 \\(p\\)。如果 \\( 2 \times size(u) \ge size(p)\\) 的話就繼續往上爬,並把經過的點的重心都設為向上爬的起始點。 diff --git a/src/graph/centroid_decomposition.md b/src/graph/centroid_decomposition.md index 1e7d9600..c9380231 100644 --- a/src/graph/centroid_decomposition.md +++ b/src/graph/centroid_decomposition.md @@ -8,7 +8,7 @@ ## Centroid Decomposition -在詳細介紹重心剖分之前,我們先來介紹一個新名詞—— **重心樹**,它可以幫助我們更好的了解重心剖分的性質。 +在詳細介紹重心剖分之前,我們先來介紹一個新名詞 —— **重心樹**,它可以幫助我們更好的了解重心剖分的性質。 ### 什麼是重心樹? @@ -343,7 +343,7 @@ int main() { 有一點需要稍微注意,如果將這題的查詢改成 **最遠** 的紅色點距離。我們所需要紀錄的資訊就不會這麼簡單了,因為在查詢時可能會遇到這種情況: -假設我們在查詢點 \\(x\\), \\(u\\) 為 \\(x\\) 在重心樹上的祖先,且離 \\(u\\) 最遠的紅色節點為 \\(v\\),所以我們會用 \\(x \rightarrow u \rightarrow v\\) 的距離來更新答案,但如果 \\( LCA'(x, v) \neq u \\),那麼 \\(u\\) 很可能就不在 \\(x\\) 到 \\(v\\) 的路徑上,所以得出來的答案會比正確答案來得 **大**。 +假設我們在查詢點 \\(x\\),\\(u\\) 為 \\(x\\) 在重心樹上的祖先,且離 \\(u\\) 最遠的紅色節點為 \\(v\\),所以我們會用 \\(x \rightarrow u \rightarrow v\\) 的距離來更新答案,但如果 \\( LCA'(x, v) \neq u \\),那麼 \\(u\\) 很可能就不在 \\(x\\) 到 \\(v\\) 的路徑上,所以得出來的答案會比正確答案來得 **大**。 讀者可以思考一下為什麼這種錯誤不會在求 **最近** 距離的時候發生,而這種情況要怎麼處理會在下一題例題中被提到。