1. 程式人生 > 其它 >【題解】[JOISC 2021 Day1] IOI 熱の感染拡大

【題解】[JOISC 2021 Day1] IOI 熱の感染拡大

大模擬,需要肝。

我們一步步分解。

首先最開始只有第一個個點被感染了,那麼我們最先固定第一個人的方向,有 \(4\) 種選擇。

對於第一個點初始方向上的點,一定與 \(1\) 號點相向運動,否則它們既不會被感染,也不會感染別人,沒有任何作用。我們稱這類點為 \(\mathbf{A}\) 類節點

對於第一個點初始方向的相反反向上節點,必定向 \(1\) 號點方向移動。

對於剩下點,每個點的方向有兩種選擇,第一種是垂直於 \(1\) 號點的初始方向運動,第二種是平行於 \(1\) 號點,並向 \(1\) 號點運動。

但是每種點都有兩種選擇,狀態數仍然太大了。

結論:\(1\) 號點初始值位置 \((a,b)\)

,則當前點一定選擇到直線 \(x=a\) 和直線 \(y=b\) 中較長的一個。

手動模擬一下發現向另一個走不可能被感染。

對於兩個方向相同的點,只可能垂直於初始方向運動,且可以根據相對位置 $\mathcal{O}(1) $ 判斷是否被感染。

對於原本就在直線 \(x=a\) 或直線 \(y=b\) 的直線,只可能向 \(1\) 號點方向運動 。

這樣我們就固定了所有點的運動方向。

垂直於初始方向運動的點,我們稱為 \(\mathbf{C}\) 類點。平行運動的我們稱為 \(\mathbf{D}\) 號點。

那麼什麼情況下會發生傳染?

首先被 \(1\) 號點傳染的,一定是在初始方向上的,或在過 \(1\)

點,且斜率為 \(1\)\(-1\) 的直線上。 注意這是必要條件,並不充分。

那麼兩個點之間發生傳染,過兩點直線的斜率為 \(1/-1\) ,或過兩點直線平行座標軸。

基於一個顯而易見的事實,\(i\) 被感染後才能傳染別人,所以我們可以運用類似於最短路演算法, \(d_i\) 表示節點 \(i\) 被感染的時間。然後討論感染別人的三個方向的點。

用堆優化 \(\rm Dijkstra\) 演算法即可。

但是直接建圖的邊數可能是 \(n^2\) 的。一個小優化是直線上出現的同向的點,或垂直同向的點可以直接略過。這樣每對相向的點只能只會被更新一次,邊數為 \(\mathcal{O}(n)\)

,總時間複雜度 \(\mathcal{O}(n\log n)\)

優化的具體做法是對於每條直線 \(x=k\)\(y=k\)\(y=x\)\(y=-x\) ,從左向右排序,然後掃一遍,同時記錄各個初始方向的第一個點 。