Codeforces 🟢 CF2053E. Resourceful Caterpillar Sequence
題目的難度顏色使用 Luogu 上的分級,由簡單到困難分別為 🔴🟠🟡🟢🔵🟣⚫。
🔗 CF2053E Resourceful Caterpillar Sequence
Problem Statement
題目簡述
給定一棵 個節點的樹。定義一個「毛毛蟲」由 表示,佔據從 到 的簡單路徑上的所有節點。
Nora 和 Aron 輪流移動毛毛蟲,Nora 先手。
- Nora 的回合:選擇一個與 相鄰但不被毛毛蟲佔據的節點 ,將整個毛毛蟲向 方向移動一步。
- Aron 的回合:選擇一個與 相鄰但不被毛毛蟲佔據的節點 ,將整個毛毛蟲向 方向移動一步。
當 變成葉子時 Nora 獲勝;當 變成葉子時 Aron 獲勝。若初始時 同時為葉子,或遊戲超過 輪未結束,則平局。
求有多少個有序對 ()使得 Aron 獲勝。
Constraints
約束條件
思路:換根 DP
後手必勝局面分析
關鍵觀察:勝負在兩輪內決定
假設某玩家在第 輪沒有必勝策略,但在第 輪有必勝策略。這是不可能的,因為對手總是可以 撤銷 上一輪的移動,使狀態回到第 輪之前。
因此:如果有人獲勝,必定在前 2 輪內決定。
為了方便後續討論,我們先計算每個節點 到最近葉子節點的距離,記為 (葉子本身的距離為 0)。
根據 的值,將所有節點分為三類:
- 葉子集合 (Leaf):。即樹上的葉子。
- 葉子鄰居集合 (Neighbor):。即與葉子直接相鄰的非葉子節點。
- 中心集合 (Center):。即距離任何葉子都至少 2 步的節點。
圖示範例
graph TD
subgraph "節點分類"
C3_1(("C<br>dist=3")):::center
C2_1(("C<br>dist=2")):::center
C2_2(("C<br>dist=2")):::center
N1(("N<br>dist=1")):::neighbor
N2(("N<br>dist=1")):::neighbor
N3(("N<br>dist=1")):::neighbor
N4(("N<br>dist=1")):::neighbor
L1(("L<br>dist=0")):::leaf
L2(("L<br>dist=0")):::leaf
L3(("L<br>dist=0")):::leaf
L4(("L<br>dist=0")):::leaf
L5(("L<br>dist=0")):::leaf
C3_1 --- C2_1
C3_1 --- C2_2
C2_1 --- N1
C2_1 --- N2
C2_2 --- N3
C2_2 --- N4
N1 --- L1
N2 --- L2
N3 --- L3
N3 --- L4
N4 --- L5
end
classDef leaf fill:#90EE90,stroke:#228B22,color:#000
classDef neighbor fill:#FFD700,stroke:#DAA520,color:#000
classDef center fill:#87CEEB,stroke:#4682B4,color:#000
- 🟢 綠色 (L):葉子節點,
- 🟡 黃色 (N):葉子的鄰居,
- 🔵 藍色 ©:中心節點,
獲勝條件分析
根據「勝負在兩輪內決定」的觀察,Aron 要獲勝,必須滿足以下兩個條件之一:
Case 1:初始必勝(第 0 輪)
- 遊戲開始時, 已經是葉子,Aron 直接獲勝。
- 注意:若 也是葉子(即 都是葉子),則根據規則是平局,不算 Aron 獲勝。
- 計數: 從 個葉子中選, 從 個非葉子中選,共 對。
Case 2:過程必勝(第 2 輪)
當初始狀態 都不是葉子時,需要進一步分析:
- Nora 想在第 1 輪獲勝,她需要將 移動到某個葉子 。
- 這要求 有一個相鄰的葉子,即 ()。
- 因此,若 (),Nora 第 1 輪無法獲勝。
詳細推導:
- 遊戲開始時, 都不是葉子,無人獲勝。
- Nora 的第 1 輪:Nora 必須移動頭部 。由於 , 的所有鄰居都不是葉子,Nora 無法立即獲勝。無論 Nora 怎麼移動,毛毛蟲整體會「滑動」一步:
- 新的頭部變成 的某個鄰居 。
- 尾部 會收縮到路徑上的下一個節點 (即原本 往 方向的鄰居)。
- Aron 的第 2 輪:此時尾部在 。若 (),則 有一個相鄰的葉子 。Aron 可以將尾部移動到 ,獲勝!
- 關鍵洞察:Nora 無法阻止尾部收縮到 。無論她選擇往哪個方向移動頭部,尾部的收縮方向是固定的(沿著 的路徑)。
為何 必須在 中?
- 若 ( 是葉子):這不可能發生。因為 位於 路徑上, 至少有兩個鄰居( 側和 側),故 的度數 ,不是葉子。唯一的例外是毛毛蟲長度為 2(即 ),但若 是葉子則 Nora 在第 0 輪就獲勝,這已包含在 Case 1 中且與 矛盾。
- 若 (): 旁邊沒有葉子,Aron 無法在第 2 輪獲勝。根據「勝負在兩輪內決定」,此時遊戲將平局。
- 所以 是 Aron 在第 2 輪獲勝的必要條件。
毛毛蟲移動示意圖
- 粗黑框:毛毛蟲佔據的節點
- ★:頭部、●:尾部
- 顏色表示節點類型:🟢L / 🟡N / 🔵C / ⬜其他
初始狀態
頭 ,尾 ,路徑上有
graph LR
subgraph "初始狀態"
direction LR
Other1((" · ")):::other
U1((" u ")):::center
P1((" p★ ")):::caterpillar_C
M1((" · ")):::caterpillar_other
M2((" · ")):::caterpillar_other
X1((" x ")):::caterpillar_N
Q1((" q● ")):::caterpillar_N
More1((" · ")):::other
L1((" ℓ ")):::leaf
Other1 ~~~ U1
U1 --- P1
P1 --- M1
M1 --- M2
M2 --- X1
X1 --- Q1
Q1 ~~~ More1
X1 --- L1
end
classDef leaf fill:#90EE90,stroke:#228B22,color:#000
classDef neighbor fill:#FFD700,stroke:#DAA520,color:#000
classDef center fill:#87CEEB,stroke:#4682B4,color:#000
classDef other fill:#D3D3D3,stroke:#808080,color:#666
classDef caterpillar_C fill:#87CEEB,stroke:#000,stroke-width:4px,color:#000
classDef caterpillar_N fill:#FFD700,stroke:#000,stroke-width:4px,color:#000
classDef caterpillar_other fill:#D3D3D3,stroke:#000,stroke-width:4px,color:#000
- 毛毛蟲佔據:(5 個節點,粗黑框)
Nora 移動後
頭移動到 ,尾收縮到
graph LR
subgraph "Nora 移動後"
direction LR
Other2((" · ")):::other
U2((" u★ ")):::caterpillar_C
P2((" p ")):::caterpillar_C
M3((" · ")):::caterpillar_other
M4((" · ")):::caterpillar_other
X2((" x● ")):::caterpillar_N
Q2((" q ")):::neighbor
More2((" · ")):::other
L2((" ℓ ")):::leaf
Other2 ~~~ U2
U2 --- P2
P2 --- M3
M3 --- M4
M4 --- X2
X2 ~~~ Q2
Q2 ~~~ More2
X2 --- L2
end
classDef leaf fill:#90EE90,stroke:#228B22,color:#000
classDef neighbor fill:#FFD700,stroke:#DAA520,color:#000
classDef center fill:#87CEEB,stroke:#4682B4,color:#000
classDef other fill:#D3D3D3,stroke:#808080,color:#666
classDef caterpillar_C fill:#87CEEB,stroke:#000,stroke-width:4px,color:#000
classDef caterpillar_N fill:#FFD700,stroke:#000,stroke-width:4px,color:#000
classDef caterpillar_other fill:#D3D3D3,stroke:#000,stroke-width:4px,color:#000
- 毛毛蟲佔據:(粗黑框)
- 尾部 ,旁邊有葉子
Aron 移動後(獲勝!)
graph LR
subgraph "Aron 獲勝"
direction LR
Other3((" · ")):::other
U3((" u ")):::center
P3((" p★ ")):::caterpillar_C
M5((" · ")):::caterpillar_other
M6((" · ")):::caterpillar_other
X3((" x ")):::caterpillar_N
L3((" ℓ● ")):::caterpillar_L
Other3 ~~~ U3
U3 --- P3
P3 --- M5
M5 --- M6
M6 --- X3
X3 --- L3
end
classDef leaf fill:#90EE90,stroke:#228B22,color:#000
classDef center fill:#87CEEB,stroke:#4682B4,color:#000
classDef neighbor fill:#FFD700,stroke:#DAA520,color:#000
classDef other fill:#D3D3D3,stroke:#808080,color:#666
classDef caterpillar_C fill:#87CEEB,stroke:#000,stroke-width:4px,color:#000
classDef caterpillar_N fill:#FFD700,stroke:#000,stroke-width:4px,color:#000
classDef caterpillar_L fill:#90EE90,stroke:#000,stroke-width:4px,color:#000
classDef caterpillar_other fill:#D3D3D3,stroke:#000,stroke-width:4px,color:#000
- 尾部 是葉子( 類)→ Aron 獲勝!
換根 DP 計數
我們需要統計 Case 2 的數量。回顧條件:
- ()
- ()
- 往 方向的第一個鄰居 ()
對於每個 (非葉子),我們要計算有多少個 滿足:從 往 方向走的第一個鄰居 恰好屬於 。
換句話說,對於 的每個鄰居 :
- 若 ,則「從 經過 能到達的所有節點」中的 類節點都是合法的 。
- 這等價於以 為根時, 子樹內的 類節點。
這是一個典型的「對每個節點,統計各方向子樹內某類節點數量」的問題,適合用 換根 DP 解決。
狀態定義
以任意節點(如節點 0)為根建樹後:
| 狀態 | 定義 |
|---|---|
sz[u] |
以 為根的子樹中, 類節點的數量(即 的節點數)。 |
f[u] |
當 時,滿足 Case 2 條件的合法 的總數。 |
cnt[2] |
整棵樹中 類節點的總數(預處理得到)。 |
整體流程
Step 1:BFS 預處理距離
從所有葉子開始做多源 BFS,計算每個節點到最近葉子的距離 dist[u]。同時統計 cnt[0]、cnt[1]、cnt[2] 分別為 、、 類節點的數量。
Step 2:第一次 DFS(Bottom-up)
sz[u] 和來自子節點方向的貢獻對於節點 ,遍歷其所有子節點 :
- 累加子樹大小:
sz[u] += sz[v] - 計算貢獻:若 (
dist[v] == 1),則 是 往子樹方向的鄰居。此時 子樹內的所有 類節點都是合法的 (因為從 往這些 走,第一步就是走到 )。故f[u] += sz[v]。
Step 3:第二次 DFS(Top-down / Reroot)
當我們從 走到子節點 時,從 的視角來看, 變成了 的一個鄰居(往「父節點方向」)。
若 (dist[u] == 1),則 方向的所有 類節點都是 的合法 。
方向的 類節點數量 = 整棵樹的 類總數 - 子樹內的 類數量 = cnt[2] - sz[v]
故 f[v] += (cnt[2] - sz[v])。
Step 4:計算最終答案
- Case 1:,,共
cnt[0] * (n - cnt[0])對。 - Case 2:對所有非葉子節點 (),累加
f[u]。
複雜度分析
- 時間複雜度:。標記距離、兩次 DFS 遍歷均為線性時間。
- 空間複雜度:。用於儲存圖、距離陣列與 DP 狀態。
Code
1 | from collections import deque |
寫在最後
PROMPT
這裡什麼都沒有。
對於將 Recursion DFS 轉換為 Iteration DFS 已經越來越熟練了。
靈機一動想要在筆記加上圖示範例,結果陷入了大坑之中。即使用了 AI 輔助還是一直給我錯誤的結構圖,還是得自己手動調整。








