rating: 1982
Problem Statement
本題為一道 Run-Twice 類型的通信題 (Communication Problem)。
關於通信題 (Communication Problems)
這是一類新的題目類型,程式會被執行 兩次 ,且遵循以下規則:
獨立執行 :兩次執行之間的記憶體與變數狀態不會保留 。
溝通機制 :第一次執行的輸出內容,某種程度上會影響或成為第二次執行的輸入資訊(視題目而定),這是兩階段間唯一的溝通橋樑。
輸入輸出 :兩次執行的輸入與輸出格式通常不同。
資源限制 :兩次執行的時間與空間限制是分開計算 的。例如時限 2 秒,只有當單次執行超過 2 秒才會被判 TLE(如果兩次各用 1.5 秒則是合法的)。
互動性 :此類題目也可能同時是互動題 (Interactive),需注意 IO 格式與 Flush 操作。
題目簡述
給定一個二分連通圖,節點數 n n n ,邊數 m m m 。
第一階段 (Player A) :得知完整圖結構,需將每個頂點染成 R, G, B 三色之一。
第二階段 (Player B) :被隨機放置在 v v v (v ≠ 1 v \neq 1 v = 1 ),無法得知自身編號與顏色,僅能看見所有鄰居的顏色。需根據鄰居顏色選擇移動到一個更靠近節點 1 1 1 的鄰居。
Constraints
約束條件
2 ≤ n ≤ 1 0 5 , n − 1 ≤ m ≤ 1 0 5 2 \le n \le 10^5, n-1 \le m \le 10^5 2 ≤ n ≤ 1 0 5 , n − 1 ≤ m ≤ 1 0 5
1 ≤ q ≤ 1 0 5 1 \le q \le 10^5 1 ≤ q ≤ 1 0 5
∑ d ( v ) ≤ 2 ⋅ 1 0 5 \sum d(v) \le 2 \cdot 10^5 ∑ d ( v ) ≤ 2 ⋅ 1 0 5
圖保證為二分圖且連通。
第二階段的輸入是 非適應性 (Non-adaptive) 的。
這意味著 Player B 面對的詢問(起始位置)是固定的,不會因為 Player A 的染色方式不同而改變。換句話說,Judge 不會刻意針對你的染色結果去構造「最難」的詢問。
思路:BFS 分層染色
策略分析
由於我們需要引導 Player B 往節點 1 1 1 移動,最直觀的想法是利用節點 1 1 1 為其建立導航資訊 。
我們可以使用 BFS 計算每個節點 u u u 到節點 1 1 1 的最短距離 d i s t ( u ) dist(u) d i s t ( u ) 。
由於無法直接傳遞距離數值,我們利用 模運算 將距離映射到三種顏色:
C o l o r ( u ) = { Red if d i s t ( u ) ≡ 0 ( m o d 3 ) Green if d i s t ( u ) ≡ 1 ( m o d 3 ) Blue if d i s t ( u ) ≡ 2 ( m o d 3 ) Color(u) =
\begin{cases}
\text{Red} & \text{if } dist(u) \equiv 0 \pmod 3 \\
\text{Green} & \text{if } dist(u) \equiv 1 \pmod 3 \\
\text{Blue} & \text{if } dist(u) \equiv 2 \pmod 3
\end{cases}
C o l o r ( u ) = ⎩ ⎪ ⎪ ⎨ ⎪ ⎪ ⎧ Red Green Blue if d i s t ( u ) ≡ 0 ( m o d 3 ) if d i s t ( u ) ≡ 1 ( m o d 3 ) if d i s t ( u ) ≡ 2 ( m o d 3 )
為什麼這樣可行?
題目保證圖是 二分圖 (Bipartite Graph) 。
在二分圖中,從一點出發的 BFS 分層結構非常嚴謹:任意邊 ( u , v ) (u, v) ( u , v ) 連接的兩點,其距離起點 1 1 1 的距離差恰為 1 1 1 。即 ∣ d i s t ( u ) − d i s t ( v ) ∣ = 1 |dist(u) - dist(v)| = 1 ∣ d i s t ( u ) − d i s t ( v ) ∣ = 1 。
這意味著,對於位於距離 d d d 的節點 v v v (目前所在位置),其鄰居的距離只能是 d − 1 d-1 d − 1 (父節點方向) 或 d + 1 d+1 d + 1 (子節點方向)。
根據 d ( m o d 3 ) d \pmod 3 d ( m o d 3 ) 的染色規則,相鄰節點的顏色必然不同,且循環順序固定:
⋯ → R → G → B → R → … \dots \to R \to G \to B \to R \to \dots ⋯ → R → G → B → R → …
若 C o l o r ( v ) = R Color(v) = R C o l o r ( v ) = R (0 0 0 ),鄰居可能是 B B B (2 2 2 , 父) 或 G G G (1 1 1 , 子)。
若 C o l o r ( v ) = G Color(v) = G C o l o r ( v ) = G (1 1 1 ),鄰居可能是 R R R (0 0 0 , 父) 或 B B B (2 2 2 , 子)。
若 C o l o r ( v ) = B Color(v) = B C o l o r ( v ) = B (2 2 2 ),鄰居可能是 G G G (1 1 1 , 父) 或 R R R (0 0 0 , 子)。
Player B 的決策
在第二階段,Player B 看到鄰居顏色集合 S S S 。由於 v ≠ 1 v \ne 1 v = 1 ,必然存在至少一個父節點 (d i s t = d − 1 dist = d-1 d i s t = d − 1 ),即 S S S 中一定包含父節點的顏色。
若 ∣ S ∣ = 1 |S| = 1 ∣ S ∣ = 1 :
由於父節點 (d − 1 d-1 d − 1 ) 與子節點 (d + 1 d+1 d + 1 ) 的顏色差為 2 ( m o d 3 ) 2 \pmod 3 2 ( m o d 3 ) ,它們的顏色 絕對不同 。
因此,若只看到一種顏色,說明所有鄰居都是父節點(或者剛好沒有子節點)。
決策:直接移動到該顏色對應的任意鄰居 。
若 ∣ S ∣ = 2 |S| = 2 ∣ S ∣ = 2 :
看到 { G , B } \{G, B\} { G , B } :自己必為 R R R (0 0 0 )。父節點為 B B B (2 2 2 )。→ \to → 往 Blue 走 。
看到 { R , B } \{R, B\} { R , B } :自己必為 G G G (1 1 1 )。父節點為 R R R (0 0 0 )。→ \to → 往 Red 走 。
看到 { R , G } \{R, G\} { R , G } :自己必為 B B B (2 2 2 )。父節點為 G G G (1 1 1 )。→ \to → 往 Green 走 。
複雜度分析
時間複雜度:O ( N + M ) \mathcal{O}(N+M) O ( N + M ) 用於第一階段 BFS;第二階段每個 Query 為 O ( d ( v ) ) \mathcal{O}(d(v)) O ( d ( v ) ) 。
空間複雜度:O ( N + M ) \mathcal{O}(N+M) O ( N + M ) 儲存圖與顏色。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 from collections import dequeCOLORS = "rgb" def solve (player ): if player == "first" : n, m = map (int , input ().split()) g = [[] for _ in range (n)] for _ in range (m): u, v = map (lambda x: int (x) - 1 , input ().split()) g[u].append(v) g[v].append(u) color = ['' ] * n vis = [False ] * n vis[0 ] = True q = deque([(0 , 0 )]) while q: u, d = q.popleft() color[u] = COLORS[d % 3 ] for v in g[u]: if not vis[v]: vis[v] = True q.append((v, d + 1 )) print ("" .join(color)) else : q = int (input ()) for _ in range (q): d = int (input ()) s = input () assert len (s) == d colors = sorted (set (s)) if len (colors) == 1 : c = colors[0 ] elif len (colors) == 2 : if 'g' in colors and 'b' in colors: c = 'b' elif 'r' in colors and 'b' in colors: c = 'r' elif 'r' in colors and 'g' in colors: c = 'g' print (s.find(c) + 1 ) if __name__ == "__main__" : player = input () t = int (input ()) for _ in range (t): solve(player)
寫在最後
PROMPT