[题解] 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;
}
posted @ 2021-07-20 11:45  Last_Breath  阅读(77)  评论(0编辑  收藏  举报