slope trick

Hugoi發表於2024-10-23

P4597 序列 sequence

首先考慮 \(dp\)

由於只需將序列改為非嚴格遞增,那麼就有一個貪心,即最終答案的數集不會變大。

為什麼呢?

這是因為只有序列某一位置嚴格遞減時,才會進行修改。

修改可以將前面的數降到和後面的數一樣大,或者將後面的數提到和前面的數一樣大。

而這樣只會讓數集變小或不變。

那麼設 \(f[i][j]\) 表示將第 \(i\) 個數變為 \(a[j]\) 並使序列非嚴格遞增的最小代價。

\(f[i][j]=\min(f[i-1][k]+|a_i-a_k|)\)

時間複雜度 \(O(n^2)\)

無法透過此題。

為了方便理解,將 \(dp\) 陣列拆為兩個來看。

\(f[i][j]\) 表示前 \(i\) 個數,將第 \(i\) 個數改為 \(\le j\) 並使序列非嚴格遞增的最小代價。

\(g[i][j]\) 表示前 \(i\) 個數,將第 \(i\) 個數改為 \(j\) 並使序列非嚴格遞增的最小代價。

\(g[i][j]=f[i-1][j]+|a_i-j|\)

\(f[i][j]=\min_{k\le j}(g[i][k])\)

每次固定 \(i\) ,以 \(j\) 為橫座標,函式值為縱座標,構建平面直角座標系。

\(G_i\) 表示當 \(i\) 確定後的 \(g\) 函式影像, \(F_i\) 表示當 \(i\) 確定後的 \(f\) 函式影像。

\(G_1\):

\(F_1\):

\(G_2\) 可由 \(F_1\) 轉移而來,即在 \(F_1\) 上加一個絕對值函式。

那麼 \(F_2\) 就是

或者 \(G_2\)

那麼 \(F_2\) 就是

可以發現,\(F_i\) 是一個斜率遞增,公差為 \(1\) ,最後一段與 \(x\) 軸平行的凸函式,\(G_2\) 是一個斜率單調遞增,最後一段斜率為 \(1\) 的凸函式。

而答案就是 \(G_n/F_n\) 最低點距離 \(x\) 軸的距離。

那麼如何維護這兩個下凸函式呢?

首先考慮計算答案時需要什麼。

可以發現只有 \(a_i\) 比當前決策點小時答案會更新。

從絕對值函式的轉折點開始考慮。

轉折點處的函式值一定不變,因為轉折點的縱座標為 \(0\)

轉折點前的所有函式斜率 \(-1\) ,但決策點橫座標不變。

轉折點後的所有函式斜率 \(+1\) ,決策點橫座標同樣不變。

原函式為 \(BAGF\),加上了函式 \(JCK\),變成了函式 \(BMNO\)

那麼對答案的貢獻即為 \(M\)\(G\) 的縱座標之差。

而縱座標之差可以分為 \(MA\)\(NG-MA\) 來求。

由於 \(BM\)\(BA\) 的斜率差為 \(1\) ,所以橫座標之差等於縱座標之差。

由於 \(MN\)\(AG\) 的斜率差為 \(1\) ,所以橫座標之差等於將 \(MN\) 向下平移到 \(A\) 時的 \(NG\) 長度。

這樣全部加起來,答案就是新決策點與原決策點的縱座標之差。

所以我們需要知道所有的 \(\leq0\) 的斜率來維護每次決策點的更新,並計算答案。

如何維護?

首先看 \(a_i\) 在原決策點右側或原決策點位置的情況。

那就是左側所有函式的斜率 \(-1\) ,右側最終取完 \(\min\) 後斜率還是 \(0\)

所以可以想到維護一個優先佇列,每次插入 \(a_i\) 表示有一條與 \(x\) 軸平行的橫座標從 \(a_i\)\(+\infty\) 的射線。

那麼優先佇列中的斜率是什麼呢?

可以發現,此時優先佇列中點的斜率就是其在優先佇列中排名 \(-1\) 的相反數。

但是,如果相鄰兩個點間的斜率差 \(>1\) ,怎麼表示?

那就插入一個點多次。

現在,優先佇列中的斜率就變成了與當前點數值相等的最左側的點在優先佇列中的排名-1的相反數

如果 \(a_i\) 在原決策點左側,又該如何維護優先佇列呢?

對原函式的改動還是將 \(a_i\) 左側的函式斜率 \(+1\),右側的函式斜率 \(-1\)

相當於在 \(a_i\) 位置插入兩個線段,因為當前位置斜率改變了 \(2\)

就完了。

P4331

嚴格遞增,並要求輸出方案。

對於嚴格遞增,只需要先將 \(a_i-i\) ,最後再加回去即可轉化為非嚴格遞增。

如何輸出方案?

先給出結論:每一項先取 \(q.top\) ,最後做一遍字尾 \(\min\) 即可。

首先對於最後一項,取 \(q.top\) 一定存在一種最優方案。

對於前一項,如果比後一項大,不妨將其變的和後一項一樣大,這樣是保證最優的。

因為每個點取的都是 \(q.top\) ,而這是每個點最小的最優可能值。

如果比後一項小,那麼對於這一項到第一項,以當前項的 \(q.top\) 為結尾一定是最優的。

所以每次記錄下來 \(q.top\) ,最後做一遍字尾 \(\min\)即可。

練習題

ABC250G

CF865D

ARC070E

P3642 煙火表演

P4272 序列變換

ABC217H

CF1534G