Codeforces 387E 要求從數列中刪除指定元素,每次刪除代價為當前區間長度,求最小總代價。本文解析「逆向思維 + 有序集合維護區間」的核心解法:按值從小到大加入,用 set 維護已加入位置,線段樹查詢區間長度。
set<int> S。upper_bound 找到其左右鄰近的已保留位置,確定區間 [L, R]。pos[val]:值 val 在原數列中的位置。set<int> S:已加入保留元素的位置集合,支持 O(log n) 前驅/後繼查詢。// 初始化線段樹:所有位置為1(初始全保留)
build(1, 1, n);
set<int> S;
S.insert(0); S.insert(n+1); // 哨兵
long long ans = 0;
for (int val = 1; val <= n; ++val) {
if (toDelete[val]) {
int p = pos[val];
auto itR = S.upper_bound(p);
int R = *itR - 1;
int L = *(--itR) + 1;
ans += query(1, 1, n, L, R); // 區間長度
update(1, 1, n, p, 0); // 標記刪除
} else {
S.insert(pos[val]); // 保留元素加入集合
}
}
printf("%lld\n", ans);
本題展示了「逆向構造 + 有序集合定位區間 + 線段樹維護動態統計」的經典組合。核心技巧:
set.upper_bound O(log n) 找到區間邊界。此模式廣泛適用於「動態區間統計 + 离線處理」問題。