[題解] CF609F Frogs and mosquitoes
阿新 • • 發佈:2021-07-20
前言
題意
有 \(n\) 個區間,每個區間為 \([x_i,x_i+t_i]\) ,有 \(m\) 個事件,事件的位置為 \(p_j\) ,每個事件會被 \(x_i+t_i\geq p_j\) 的區間中, \(x_i\) 最小的區間所處理,處理後 \(t_i\) 會增加 \(b_j\)。事件如果沒有處理,則不會消失。下一個事件出現時,當且僅當沒有可以處理的事件。
輸出每個區間處理的事件,和最終的 \(t_i\) 。
思路
首先可以發現,這些區間的左端點都是不變的,因為題目想要最小的 \(x_i\) ,所以對於這些區間按照左端點排序。
可以發現,當前答案這一位為 \(k\)
而找到這個分界點,就可以找到能處理這個事件的區間,只需要找到能夠處理區間最大值的資料結構。
這裡使用的是線段樹。第一個二分,二分出 \(x_i\) 大於 \(p_j\) 的數字的前驅,設為 \(rmax\) 。然後再 \([1,rmax]\) 中二分當前答案。若 \([1,mid]\) 中,最大的 \(x_i+t_i\) 大於 \(p_j\) 則代表前 \(mid\) 個區間可以處理這個事件。
若沒有區間可以處理這個事件,則用一個 multiset 來維護沒有被處理的事件,按照 \(p_j\)
否則處理完當前的事件,區間變長了,由於只有這一個區間變長,所以只有這個區間可能處理之前沒處理過的事件。
因為用了 multiset ,所以按順序列舉 multiset 中的元素。知道不能處理下一個事件或是沒有事件可以處理為止。
每處理一個事件,都放入 \(slay\) 這個 multiset 中,所有可處理的事件處理完之後,在進行刪除,否則會炸。
每次處理後 \(slay\) 陣列需要清空。
Code
時間複雜度為 \(O(n\log n)\) 。
#include <set> #include <cstdio> #include <algorithm> using namespace std; #define LL long long const int MAXN = 2e5 + 5; multiset<pair<int, int> > st, slay; struct Node { int l, r, id; friend bool operator < (Node x, Node y) { return x.l < y.l; } }; bool cmp(Node x, Node y) { return x.id < y.id; } Node a[MAXN]; int ans[MAXN], p[MAXN], b[MAXN], n, m; LL len[MAXN]; struct Segment_Node { int l, r; LL maxdata; #define ls (pos << 1) #define rs (pos << 1 | 1) }; struct Segment_Tree { Segment_Node t[MAXN << 2]; void Push_Up(int pos) { t[pos].maxdata = max(t[ls].maxdata, t[rs].maxdata); } void Build(int pos, int l, int r) { t[pos].l = l, t[pos].r = r; if (l == r) { t[pos].maxdata = a[l].r; return; } int mid = (l + r) >> 1; Build(ls, l, mid); Build(rs, mid + 1, r); Push_Up(pos); } void Update(int pos, int x, int d) { if (t[pos].l == t[pos].r) { t[pos].maxdata = d; return; } if (t[ls].r >= x) Update(ls, x, d); else Update(rs, x, d); Push_Up(pos); } int Query_Max(int pos, int l, int r) { if (l <= t[pos].l && t[pos].r <= r) return t[pos].maxdata; int res = 0; if (l <= t[ls].r) res = max(res, Query_Max(ls, l, r)); if (r >= t[rs].l) res = max(res, Query_Max(rs, l, r)); return res; } }; Segment_Tree tree; int main() { scanf("%d %d", &n, &m); for (int i = 1, x, t; i <= n; i++) { scanf("%d %d", &x, &t); a[i].l = x, a[i].r = x + t, a[i].id = i; } for (int i = 1; i <= n; i++) len[i] = a[i].r; sort(a + 1, a + 1 + n); tree.Build(1, 1, n); for (int i = 1; i <= m; i++) { scanf("%d %d", &p[i], &b[i]); int l = 1, r = n, rmax = -1, res = -1; while (l <= r) { int mid = (l + r) >> 1; if (a[mid].l <= p[i]) l = mid + 1, rmax = mid; else r = mid - 1; } l = 1, r = rmax; while (l <= r) { int mid = (l + r) >> 1; if (tree.Query_Max(1, 1, mid) >= p[i]) r = mid - 1, res = mid; else l = mid + 1; } if (res == -1) st.insert(make_pair(p[i], b[i])); else { LL maxlen = len[a[res].id] + b[i]; ans[a[res].id]++; for (multiset<pair<int, int> >::iterator it = st.upper_bound(make_pair(a[res].l, 0)); it != st.end(); it++) { if (it->first <= maxlen) { maxlen += it->second; ans[a[res].id]++; slay.insert(*it); } else break; } for (multiset<pair<int, int> >::iterator it = slay.begin(); it != slay.end(); it++) st.erase(*it); slay.clear(); len[a[res].id] = maxlen; tree.Update(1, res, maxlen); } } sort(a + 1, a + 1 + n, cmp); for (int i = 1; i <= n; i++) printf("%d %lld\n", ans[i], len[i] - a[i].l); return 0; }