設\(f[i][j]\)表示前\(i\)個數被恰好分為\(j\)個單調區間的最小花費,有\(f[i][j]=\overset{i-1}{\underset{p=1}{\min}}(f[p][j-1]+\text{cost}[p+1][i])\),其中\(\text{cost}[i][j]\)表示區間\([i,j]\)分成一個單調序列的最小花費。於是問題轉化成了求\(\text{cost}\);而由題意不難知道這就是上面一道題目(只不過我們還需要求單調下降的情況,這裡有個trick,直接將原數列所有數取相反數再求單調上升即可,從答案集合的角度可以知道是正確的)
然而,我們不能在外界迴圈\(i,j\),然後透過\(O((j-i+1)\log (j-i+1))\)的複雜度去算\(\text{cost}[i][j]\),這樣會超時的。我們只能想辦法利用之前已經計算過的資訊。假設當前計算的是單調上升。我們外層列舉\(i\),然後跑一次左偏樹合併,當內層跑到\(j\)的時候,假設我們已經計算好了\(\text{cost}[i][i]\)到\(\text{cost}[i][j-1]\),那麼對於\(\text{cost}[i][j]\)來說,我們只用記錄最後一段區間的貢獻(這個需要記錄左偏樹的所有節點的權值和),再利用之前已經算好了的\(\text{cost}\)即可(子問題具有最優性)
需要注意的是,\(\text{cost}\)應該開三維,最後一維用來記錄是單調上升還是單調下降,因為兩者計算的時候不應該互相干擾,如果不再開一維的話,可以計算單調上升的時候會用到單調下降的\(\text{cost}\),這樣肯定就錯了
具體見打卡程式碼