求最長遞增子序列(LIS)長度。N ≤ 100000,需 O(n log n) 算法。與 POJ 2533 同模型。
維護數組 d[len]:長度為 len 的遞增子序列的最小結尾值。遍歷數組 a[i]:
a[i] > d[max_len],延長:d[++max_len] = a[i]。d[pos] ≥ a[i],更新 d[pos] = a[i]。最終 max_len 即 LIS 長度。d 數組始終嚴格遞增,保證二分有效性。
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 100005;
int a[MAXN], d[MAXN];
int main() {
int n;
while (~scanf("%d", &n)) {
for (int i = 0; i < n; ++i) scanf("%d", &a[i]);
int len = 0;
for (int i = 0; i < n; ++i) {
int pos = lower_bound(d, d + len, a[i]) - d;
d[pos] = a[i];
if (pos == len) ++len;
}
printf("%d\n", len);
}
return 0;
}
lower_bound 返回第一個 ≥ a[i] 的位置,對應「非嚴格遞增」子序列(允許相等)。若要求嚴格遞增,用 upper_bound(第一個 > a[i])。d 數組只存結尾值,不存實際序列。若需輸出具體 LIS,需額外記錄前驅指針。len=0,d 無需初始化,lower_bound 會正確返回 0。lower_bound(≥)upper_bound(>)POJ 3903 是 LIS O(n log n) 算法的標準模板題。核心思想:用 d[len] 記錄「長度為 len 的最小結尾」,貪心地保持結尾盡可能小以便後續延長。二分查找定位更新位置,lower_bound/upper_bound 選擇決定嚴格/非嚴格。背熟此模板,所有 LIS 變體迎刃而解。