【題解】[JOISC 2021 Day1] IOI 熱の感染拡大
大模擬,需要肝。
我們一步步分解。
首先最開始只有第一個個點被感染了,那麼我們最先固定第一個人的方向,有 \(4\) 種選擇。
對於第一個點初始方向上的點,一定與 \(1\) 號點相向運動,否則它們既不會被感染,也不會感染別人,沒有任何作用。我們稱這類點為 \(\mathbf{A}\) 類節點
對於第一個點初始方向的相反反向上節點,必定向 \(1\) 號點方向移動。
對於剩下點,每個點的方向有兩種選擇,第一種是垂直於 \(1\) 號點的初始方向運動,第二種是平行於 \(1\) 號點,並向 \(1\) 號點運動。
但是每種點都有兩種選擇,狀態數仍然太大了。
結論:\(1\) 號點初始值位置 \((a,b)\)
手動模擬一下發現向另一個走不可能被感染。
對於兩個方向相同的點,只可能垂直於初始方向運動,且可以根據相對位置 $\mathcal{O}(1) $ 判斷是否被感染。
對於原本就在直線 \(x=a\) 或直線 \(y=b\) 的直線,只可能向 \(1\) 號點方向運動 。
這樣我們就固定了所有點的運動方向。
垂直於初始方向運動的點,我們稱為 \(\mathbf{C}\) 類點。平行運動的我們稱為 \(\mathbf{D}\) 號點。
那麼什麼情況下會發生傳染?
首先被 \(1\) 號點傳染的,一定是在初始方向上的,或在過 \(1\)
那麼兩個點之間發生傳染,過兩點直線的斜率為 \(1/-1\) ,或過兩點直線平行座標軸。
基於一個顯而易見的事實,\(i\) 被感染後才能傳染別人,所以我們可以運用類似於最短路演算法, \(d_i\) 表示節點 \(i\) 被感染的時間。然後討論感染別人的三個方向的點。
用堆優化 \(\rm Dijkstra\) 演算法即可。
但是直接建圖的邊數可能是 \(n^2\) 的。一個小優化是直線上出現的同向的點,或垂直同向的點可以直接略過。這樣每對相向的點只能只會被更新一次,邊數為 \(\mathcal{O}(n)\)
優化的具體做法是對於每條直線 \(x=k\),\(y=k\) ,\(y=x\) ,\(y=-x\) ,從左向右排序,然後掃一遍,同時記錄各個初始方向的第一個點 。