1. 程式人生 > 其它 >[題解] CF609F Frogs and mosquitoes

[題解] CF609F Frogs and mosquitoes

前言

題目連結:洛谷

題目連結:CodeForces

題意

\(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\)

,則前 \(k-1\) 個區間中必沒有一個滿足條件的,而 \(p(p\geq k)\) ,前 \(p\) 個區間至少有一個滿足條件,於是可以二分答案。

而找到這個分界點,就可以找到能處理這個事件的區間,只需要找到能夠處理區間最大值的資料結構。

這裡使用的是線段樹。第一個二分,二分出 \(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;
}