Educational Codeforces Round 112 E、Boring Segments
阿新 • • 發佈:2021-08-10
原題網址
https://codeforces.com/contest/1555/problem/E
題目大意
有n個區間。每個區間是[1,m]的子區間。從a可以一步走到b的充要條件是存在區間同時覆蓋[a,b]。若n個區間中取出一些區間後,可以只通過被取出的區間從1走到m,則稱這些被取出的區間組成的集合A是好的。每個區間有價值w,求一個好的集合A的所有元素中,最大價值與最小价值的差的最小值。
資料結構
線段樹,支援以下兩種操作:
- 插入或刪除區間
- 查詢當前區間集合是否為好的
若一個區間集是好的,則1.5,2.5,...,m-0.5都至少被一個區間覆蓋,區間[a,b]可以覆蓋a+0.5,...,b-0.5。所以我們可以將每個區間的右端點減一後再操作,線段樹第a個元素表示a+0.5被多少個區間覆蓋。這樣只要查詢線段樹中[1,m-1]最小值是否為0,不為0即好的。
由於m較大,必須使用懶修改方法,每個節點增加lazy值(區別於真正的val值),注意點:
- 訪問某節點時,先進行push操作(將本節點lazy值轉給val值,如果還有孩子,則lazy值傳遞給孩子,清空本節點lazy值),不管被查區間是多少(若l>r也要push,因為只要能遞迴到,就表示該點實際上表示了一個區間,需要把lazy值轉給val值)。
- 修改時,如果某個節點表示的區間全都要修改,只修改該節點lazy值,不遞迴。改完後必須進行push操作,把lazy值轉成val並傳遞給孩子。
- 遞迴返回時,將兩個孩子的val組合成本節點的val。
解題思路
本題為最優化問題。顯然儘可能選價值差較小的區間。
先將所有區間按價值排序。
用雙指標維護一個已知價值最小值,找到價值最大值最小的好的區間範圍。
列舉所有最小值,根據最小值指標和線段樹查詢結果移動最大值指標並更新最後答案(用最大值-最小值更新ans)。
我的程式碼
#include <bits/stdc++.h> using namespace std; struct seg { int l, r, w; }; bool operator<(const seg& a, const seg& b) { return a.w < b.w; } vector<seg> seglist; vector<int> seglazy; vector<int> segtree; void push(int v) { if (2 * v + 1 < segtree.size()) { seglazy[2 * v] += seglazy[v]; seglazy[2 * v + 1] += seglazy[v]; } segtree[v] += seglazy[v]; seglazy[v] = 0; } void addvalue(int v, int tl, int tr, int l, int r, int val) { push(v); if (l > r) return; if (l == tl && r == tr) { seglazy[v] += val; push(v); return; } int tm = (tl + tr) / 2; addvalue(v * 2, tl, tm, l, min(r, tm), val); addvalue(v * 2 + 1, tm + 1, tr, max(l, tm + 1), r, val); segtree[v] = min(segtree[2 * v], segtree[2 * v + 1]); } int get() { return segtree[1] + seglazy[1]; } int main() { ios::sync_with_stdio(false); int n, m; cin >> n >> m; seglist.resize(n); for (int i = 0; i < n; ++i) { cin >> seglist[i].l >> seglist[i].r >> seglist[i].w; --seglist[i].l; --seglist[i].r; } sort(seglist.begin(), seglist.end()); segtree.resize(m << 2, 0); seglazy.resize(m << 2, 0); int lp = 0, rp = 0; int ans = 1e9; for (lp = 0; lp < n; ++lp) { while (rp < n && get() == 0) { addvalue(1, 0, m - 2, seglist[rp].l, seglist[rp].r - 1, 1); ++rp; } if (get() == 0) break; ans = min(ans, seglist[rp - 1].w - seglist[lp].w); addvalue(1, 0, m - 2, seglist[lp].l, seglist[lp].r - 1, -1); } cout << ans << endl; return 0; }