查詢區間 [L, R] 中,有多少整數的「自身十進制數字序列的最長上升子序列 (LIS) 長度」等於 K。多測試案例,L, R ≤ 10¹⁸(約 18 位),K ≤ 10。
dp[pos][mask][len] = 處理到第 pos 位(從低到高),已用數字集合為 mask,當前 LIS 長度為 len 的方案數。
但實際代碼用 `dp[len][mask][lis]` 其中 `len` 為剩餘位數,`mask` 為已用集合,`lis` 為目標 K(固定),避免重複計算。
對於當前集合 `mask`,加入數字 `d`:
- 若 `d` 已在 `mask` 中 → 不改變 LIS 長度,但需更新「結尾最小值」邏輯。實際上用「貪心構造 LIS」的性質:維護每個長度的最小結尾值。因數字域極小,可直接模擬:在 `mask` 表示的序列中插入 `d`,計算新 LIS 長度與新集合。
代碼中 `find_nex` 實現:從 `d` 向上找第一個在 `mask` 中的數字 `i`,將 `i` 替換為 `d`(模擬 patience sorting 替換),若無則加入 `d`。新 mask 對應新 LIS 長度 = `popcount(new_mask)`。
ll dfs(int pos, int mask, bool leadZero, bool tight) {
if (pos == 0) return (popcount(mask) == K);
if (!tight && memo[pos][mask][K] != -1) return memo[pos][mask][K];
int up = tight ? digits[pos] : 9;
ll ans = 0;
for (int d = 0; d <= up; ++d) {
int nmask = (leadZero && d == 0) ? 0 : next[mask][d];
ans += dfs(pos-1, nmask, leadZero && d==0, tight && d==up);
}
if (!tight) memo[pos][mask][K] = ans;
return ans;
}
HDU 4352 是「數位 DP + LIS 狀態壓縮」的精妙結合。核心創新在於將 LIS 狀態壓縮為 10-bit mask 並預計算轉移,使數位 DP 狀態數極小。此技巧適用於所有「數字序列 LIS 相關屬性」的數位 DP 問題。