11.22 CW 模擬賽 T3.重複

Yorg發表於2024-11-22

演算法

考慮 \(\rm{dp}\)

其實誰都知道是 \(\rm{dp}\) , 但是推不出來啊

這個問題的關鍵點在於注意到每次往回走, 必定需要走到之前只訪問過一次的位置, 這樣演算法才有正確性

容易的, 令 \(f_i\) 表示遊覽結束前 \(i\) 個點的最小時間花費, 由上面的結論可知, 對於 \(f_i\) 往回走的更新, 一定可以用 \(f_j, j < i\) 來表示, 因為是最小的花費, 顯然 \(f_i\) 時刻在 \(i\) 點的位置

考慮轉移
注意到僅僅有這樣的情況:
先往前走到 \(i\) ( 再往後走是無意義的, 一定不優 ) , 然後在回頭走, 最後再繞回 \(i\)
這樣有一個問題在於有可能繞回來的過程中依然沒有到達時間限制 \(T\) , 於是必須要等到 \(T\) , 此時路過的點一定能夠全部訪問

在這裡我有一個誤區, 就是同時考慮了往前走在走回來這一情況, 事實上考慮一種即可覆蓋

有轉移柿子

\[f_i = \min_{j = 1}^{i - 1} \left\{ f_j + a_i - a_j + \max \left[ 2 \times (a_i - a_{j + 1}), T) \right] \right\} \]

顯然的, 有 \(f_1 = \max(a_1, T)\) , 答案即為 \(f_n\)

但是這樣做是 \(\mathcal{O} (n ^ 2)\) 的, 我們考慮最佳化

資料結構最佳化

不太顯然的, 將轉移柿子中的 \(a_i - a_j\) 提出

有,

\[f_i = a_n + \min_{j = 1}^{i - 1} \left\{ f_j + \max \left[ 2 \times (a_i - a_{j + 1}), T) \right] \right\} \]

直接丟進堆裡維護即可, 複雜度 \(\mathcal{O} (n \log n)\)

利用單調性最佳化

需要一些注意力, 我們觀察到 \(i\) 一定時, \(2 \times (a_i - a_{j + 1})\)\(f_j\) 是單調遞減的, 同時, 注意到當 \(i\) 增加時, 在之前 \(2 \times (a_i - a_{j + 1}) > T\) , 對於更大的 \(i ^ {\prime}\) 顯然也有 \(2 \times (a_{i ^ {\prime}} - a_{j + 1}) > T\)

那麼我們就可以想到一種做法, 對於每一次更新 \(a_i\) , 我們就在單調佇列中把所有滿足 \(2 \times (a_i - a_{j + 1}) > T\)\(j + 1\) 彈出, 其中 單調佇列按照 \(a_i\) 從小到大排序

彈出點的時候, 記錄其最大值 \(k\) , 然後對於佇列中的點, 有
$ f_i = a_n + f_j + T $ , 對於彈出的點, 有 \(f_i = a_n + f_j + 2 \times (a_i - k)\)

每個點進隊出隊一次, 時間複雜度 \(\mathcal{O} (n)\)

事實上因為 \(a_i\) 單調性, 完全可以使用佇列處理

程式碼

略了, 好累啊

總結

有啥好說的, 練 \(\rm{dp}\)

注意考慮轉移時不需要考慮本質相同的轉移

寫部落格就該這樣, 不能看著題解抄, 自己理一遍