Codeforces 🔵 CF2121H. Ice Baby
題目的難度顏色使用 Luogu 上的分級,由簡單到困難分別為 🔴🟠🟡🟢🔵🟣⚫。
🔗 CF2121H Ice Baby
Problem Statement
題目簡述
給定兩個長度為 的陣列 和 。
對於每個 ,考慮所有長度為 的陣列 ,滿足 ()。
請求出在所有可能的陣列 中,最長非遞減子序列 (LNDS) 的最大長度。
Constraints
約束條件
思路:資料結構維護 LIS 狀態
這是一道典型的利用資料結構優化動態規劃的題目。我們可以從最長遞增子序列(LIS)的經典解法出發,思考如何將其擴展到區間的情況。
狀態定義
在經典的 LIS 問題中,我們通常會維護一個陣列,其中第 個元素表示「長度為 的遞增子序列中,結尾元素的最小值」。
我們將這個概念套用到本題:定義一個狀態陣列,其第 個元素代表長度為 的非遞減子序列中,結尾元素的最小值。
顯然,這個狀態陣列必定是一個單調不遞減的序列。長度越長的子序列,其結尾元素的最小值必然大於等於較短子序列的結尾元素最小值。
狀態轉移分析
當我們依序處理每個區間 時,考慮它能如何更新我們的狀態陣列。
對於任意長度 ,我們可以嘗試從長度為 的子序列擴展而來。為了讓新的結尾元素盡可能小,我們應該在區間 中挑選一個大於等於前一個元素的最小值。
具體來說,假設前一個元素的值為 :
- 如果 ,我們可以直接選擇區間的下界 。
- 如果 ,我們必須選擇 本身(因為要滿足非遞減的條件)。
- 如果 ,則無法從這個狀態擴展(因為區間內的所有數都太小了)。
綜合起來,我們挑選的新結尾元素會是 ,前提是 。
接著,我們將這個新結尾元素與原本長度為 的結尾元素進行比較,取較小值來更新狀態。
轉移的整體影響
由於狀態陣列是單調不遞減的,我們可以根據當前區間的邊界 和 ,將狀態陣列的值劃分為三個部分來觀察變化:
- 的部分:
這些狀態即使接上新區間的數,得到的新結尾元素至少也是 。因為它們原本的值就已經小於等於 了,所以取最小值更新後,這部分的值完全不會改變。 - 且 的部分:
- 這部分的值可以由前一個狀態轉移而來。具體來說,這段的第一個元素會被更新為 ,後續元素則會被更新為前一個元素的值。
- 整體來看,這部分的變化相當於將整段數值向右平移一格,並在最前面插入了 。
- 的部分:
- 這部分的第一個元素(也就是 的最小元素),會因為第二部分的平移操作而被覆蓋。
- 而這部分的後續元素,由於前一個狀態已經大於 ,無法滿足非遞減條件進行有效的區間轉移,因此值不會改變。
綜合上述三個部分的變化,從整體的角度來看,狀態陣列的改變非常單純:
- 插入了一個新元素 。
- 原本在第三部分的第一個元素(也就是大於 的最小元素)被擠掉了,相當於被刪除。
如果陣列中所有元素都小於等於 ,則沒有元素會被擠掉,這意味著最長非遞減子序列的長度成功增加了 1。
資料結構維護
但在陣列中插入元素的複雜度是 ,如果直接模擬整個狀態陣列的變化,對於 個區間來說,總複雜度將達到 ,這在 高達 的情況下是不可行的。
既然我們只需要進行「插入元素」和「尋找並刪除大於某個值的最小元素」這兩種操作,我們可以使用支援動態排序的資料結構來維護這個狀態陣列。
- 在 C++ 中,可以使用
std::multiset。 - 在 Python 中,可以使用
sortedcontainers.SortedList(或自行實作平衡樹等替代方案)。
複雜度分析
- 時間複雜度:,對於每個區間,我們在
multiset中進行常數次查詢、插入和刪除操作,每次操作的時間複雜度為 。 - 空間複雜度:,
multiset最多儲存 個元素。
類題
- 🔵 P4303 [AHOI2006] 基因匹配
- 🟣 CF809D Hitchhiking in the Baltic States
本題的加強版本,差別在從 LNDS 變成 LIS,這對於使用的資料結構提出了更高的要求。
Code
這份 Python 程式碼使用 sortedcontainers.SortedList 方便測試,但其實 Codeforces 不支援這個套件。
若要正式提交,可以直接複製 SortedList 的原始碼,也能自行實作可支援插入、刪除、排名查詢的有序結構。
1 | from sortedcontainers import SortedList |
寫在最後
PROMPT
這裡什麼都沒有。







