給定序列 a[1..n],求滿足 |a[i] - a[j]| ≤ d 的非空子序列個數(模 9901)。子序列不要求連續,但下標遞增。n 未給上限,值域大,需離散化。
dp[i]:以 a[i] 結尾的合法子序列數。轉移:
dp[i] = 1 + Σ_{j
答案 = Σ dp[i]。
固定 i,需查詢值域 [a[i]-d, a[i]+d] 內所有 j 的 dp[j] 之和。離散化後用樹狀數組維护前綴和,單次查詢/更新 O(log N)。
// 複製排序去重
sort(val+1, val+n+1);
int m = unique(val+1, val+n+1) - val - 1;
// 查找離散化索引
int get_id(int x) { return lower_bound(val+1, val+m+1, x) - val; }
// 查詢區間 [L, R] 對應的離散索引範圍
int L_id = get_id(a[i] - d);
int R_id = get_id(a[i] + d); // 注意 upper_bound 邊界
const int MOD = 9901;
int bit[100010]; // 樹狀數組
void add(int idx, int val) {
for (; idx <= m; idx += idx & -idx) {
bit[idx] = (bit[idx] + val) % MOD;
}
}
int query(int idx) {
int res = 0;
for (; idx > 0; idx -= idx & -idx) {
res = (res + bit[idx]) % MOD;
}
return res;
}
int main() {
int n, d;
while (~scanf("%d%d", &n, &d)) {
// 讀入、離散化...
memset(bit, 0, sizeof(bit));
int ans = 0;
for (int i = 1; i <= n; ++i) {
int id = get_id(a[i]);
int L = get_id(a[i] - d);
int R = upper_bound(val+1, val+m+1, a[i]+d) - val - 1;
int sum = (query(R) - query(L-1) + MOD) % MOD;
int dp = (sum + 1) % MOD; // +1 為單元素子序列
add(id, dp);
ans = (ans + dp) % MOD;
}
printf("%d\n", ans);
}
return 0;
}
a[i]+d 可能不在值域中,需用 upper_bound 找 ≤ 該值的最大索引。+MOD 再 %MOD 避免負數。bit、val、m。m ≤ n,樹狀數組大小開 n+5 即可。HDU 3450 是「DP + 數據結構優化」的經典範例。當 DP 轉移為「區間和查詢」時,樹狀數組/線段樹可將 O(n²) 降為 O(n log n)。關鍵步驟:
此模板廣泛適用於「受值域約束的子序列/子數組計數」問題。