1. 程式人生 > 實用技巧 >NOI2016 王國飲水記

NOI2016 王國飲水記

NOI2016 王國飲水記

如何不寫斜優/決策單調性,用暴力通過此題。。(大霧)

首先我們只需要考慮 \(h_i>h_1\) 的點。

然後考慮 \(K\) 足夠大的點,我們手玩一段時間,發現將這些剩餘點保留下來並排序,最優決策一定是將他們依次和 \(1\) 聯通。

比如樣例中的 \(1,3,4\),假設可以操作 \(2\) 次及以上,那麼最優決策一定是依次連線 \(1,3\) 得到 \(2\),然後連線 \(2,4\) 得到 \(3\)

對於 \(K\) 並非足夠大的點,先將剩餘點從小到大排序,不難發現每次的連通操作必然是將 \(1\) 和一段區間 \([l,r]\) 聯通,同時不存在一個點被連兩次(顯然不優)

感性上理解大概可以通過調整法,知道這個性質之後我們可以寫一個 Dp 了:(方便起見,下文的 \(h\) 陣列是僅保留大於 \(h_1\) 的元素且已經排好序,下標從 \(2\) 開始的陣列,而 \(S\) 為其字首和)

\(f_{i,j}\) 表示考慮到 \([2,i]\),操作了 \(j\) 次的最優解,那麼轉移形如 \(f_{i,j}=(f_{k,j-1}+S_i-S_k)/(i-k+1)\)

然而要進行高精度小數運算,我們得到了一個 \(\mathcal O(n^2Kp)\) 的做法,非常幸運的是他可以過 \(n\le 100\) 的測試點。

然後我們將最優決策打個表,會發現大概是這樣的例子(以樣例 \(3\)

為例)

[100, 100]
[99, 99]
[98, 98]
...
[65, 66]
[62, 64]
[50, 61]

然後發現好像長度不是 \(1\) 的區間非常少?而且決策應該是從某個位置開始的字尾,都會被選中。

我們大膽猜測長度不為 \(1\) 的區間只有 \(\log\) 個(實際上是調參之後知道的),於是 Dp 的第二個維度可以降低成 \(\log\),便得到了一個 \(\mathcal O(n^2\log np)\) 的做法。(大概是預處理 Dp 陣列,然後列舉之前用了多少次,然後剩餘部分一直除以 \(2\)

然後造一點隨機資料,繼續打表,然後發現最大的區間的長度也很小?

於是轉移的時候 \(k\)

的列舉量不需要到 \([1,i]\),只需要到 \([i-c,i]\) 即可,\(c\) 為一個常數。

於是得到了一個 \(\mathcal O(n\log n\times c\cdot p)\) 的做法。

然後我們發現,之所以需要高精度小數,是因為我們經常除以 \(x\),而且除以次數高達 \(K\) 次,然而如果只考慮預處理部分的 \(O(n\log n\times c)\) 的 Dp,我們發現我們只會除以 \(\log\) 次,換而言之沒有必要用高精度小數。。。

於是這個部分用 long double 把答案存下來來比較(當然誤差很大但是不影響),然後我們將決策點記錄下來,最後還原答案的時候通過高精度實現即可。

最終複雜度是 \(\mathcal O(n\log n\times p+n\log n\times c)\),跑得非常快,我的 \(c\) 設的是 \(100\),是可以通過的。

正常的做法應該是考慮:

\[\frac{dp_j+S_i-S_j}{i-j+1}<\frac{dp_k+S_i-S_k}{i-k+1} \]

對於每個點 \(j\)\(k\) 我們將其視為 \((j-1,dp_j-S_j)\),那麼不難發現這個是最大的 \((i,S_i)\) 與點集 \(S\) 中的斜率最大值。

可以三分交點解決。