CF793F 题解

题意:

有一个长为 \(n\) 的杆,上面有 \(m\) 条绳子,每条绳子可以让蜗牛从 \(l_i\) 爬到 \(r_i\)(中途不能离开),保证 \(r_i\) 各不相同。蜗牛也可以自然下落。

现在有 \(q\) 次询问,询问 \(x\) 出发,途中高度不能低于 \(x\) 或高于 \(y\),问最高能爬到的位置。

\(n,m,q\leq 10^5\)

思路:

模拟赛场切了弱化版,信心倍增。

考虑更简单的问题(即模拟赛版本):给定若干个区间 \([l_i, r_i]\),多次询问 \([L_i, R_i]\) 能否有这些区间中的若干个的并得到。

全是区间,考虑离线。

我们一次将 \([l_i, r_i]\)\(l_i \le L\) 的线段按照右端点从小到大覆盖。我们设 \(t_i\) 表示覆盖了 \(i\) 这个点所有线段中 \(r\) 最小的。则判定条件就变得异常简单:\(\max_{i=L}^R t_i = R\) 就可行。

解释一下,如果 \(R\) 都被覆盖了还有点未被覆盖到,则说明一定有点未被覆盖,所以可以这样判定。

然后显然可以离线下来,将询问和线段都挂在左端点上,从后往前,加入一条线段相当于区间取 \(\min\),查询相当于区间 \(\max\),线段树维护即可。

现在考虑原题,延续思路,找到 \([L, R]\) 最后一个 \(\le R\) 的位置即可。

不过注意这个是边的并,\([1,2]\)\([3,4]\) 不相邻,所以要处理一下,注意细节。

这题竟然 3000*!!!!

点击查看代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm> 
using namespace std;
const int N = 5e5 + 5;

int n, m, Q;
struct Seg {
	int l, r;
	Seg (int _l = 0, int _r = 0) :
		l(_l), r(_r) {}
} a[N], q[N];  

void read(int &x) {
	x = 0;
	int f = 1;
	char ch = getchar();
	while (ch > '9' || ch < '0') {
		if (ch == '-')
			f = -1;
		ch = getchar();
	}
	while (ch <= '9' && ch >= '0')
		x = x * 10 + (ch - '0'), ch = getchar();
	x = f * x;
}

void write(int x) {
	if (x < 0)
		putchar('-'), x = -x;
	if (x > 9)
		write(x / 10);
	putchar(x % 10 + '0');
}
 
int val[N * 4] = {0};
int tag[N * 4] = {0};
struct SegTree {
	#define ls (x << 1)
	#define rs (x << 1 | 1)
	#define mid ((lx + rx) >> 1)
	void pushup(int x) {
		val[x] = max(val[ls], val[rs]);
	}
	void pushdown(int x) {
		val[ls] = min(val[ls], tag[x]);
		val[rs] = min(val[rs], tag[x]);
		tag[ls] = min(tag[ls], tag[x]);
		tag[rs] = min(tag[rs], tag[x]);
		tag[x] = 2e9;
	}
	void build(int x, int lx, int rx) {
		tag[x] = val[x] = 2e9;
		if (lx + 1 == rx)
			return;
		build(ls, lx, mid), build(rs, mid, rx);
		pushup(x);
	}
	void upd(int x, int lx, int rx, int l, int r, int v) {
		if (rx <= l || r <= lx)
			return;
		if (l <= lx && rx <= r) {
			val[x] = min(val[x], v);
			tag[x] = min(tag[x], v);
			return;
		}
		pushdown(x);
		upd(ls, lx, mid, l, r, v), upd(rs, mid, rx, l, r, v);
		pushup(x); 
	}	
	int qry(int x, int lx, int rx, int l, int r) {
		if (rx <= l || r <= lx)
			return 0;
		if (l <= lx && rx <= r)
			return val[x];
		pushdown(x);
		return max(qry(ls, lx, mid, l, r), qry(rs, mid, rx, l, r));
	}
	int fnd(int L, int R) {
		int l = L - 1, r = R + 1;
		while (l + 1 < r) {
			int M = (l + r) / 2;
			if (qry(1, 1, n + 1, L, M + 1) <= R)
				l = M;
			else
				r = M;
		}
		return l;
	} 
	SegTree () {}
	#undef ls
	#undef rs
	#undef mid 
} st;

vector<pair<int, int> > qry[N];
vector<int> seg[N];
int ans[N] = {0};
void slv() {
	for (int i = 1; i <= m; i++)
		seg[a[i].l].push_back(a[i].r);
	for (int i = 1; i <= Q; i++) 
		qry[q[i].l].push_back(make_pair(q[i].r, i)); 
	st.build(1, 1, n + 1);
	for (int i = n; i >= 1; i--) {
		for (auto j: seg[i])
			st.upd(1, 1, n + 1, i, j + 1, j);
		
	/*	for (int j = 1; j <= n; j++) 
			cout << st.qry(1, 1, n + 1, j, j + 1) << " ";
		cout << endl;*/
		for (auto j: qry[i])
			ans[j.second] = st.fnd(i, j.first) + 1;
	}
	for (int i = 1; i <= Q; i++)
		write(ans[i]), putchar('\n'); 
}

 
int main() {
	read(n), read(m);
	for (int i = 1; i <= m; i++)
		read(a[i].l), read(a[i].r), a[i].r--;
	read(Q);
	for (int i = 1; i <= Q; i++) 
		read(q[i].l), read(q[i].r), q[i].r--;
	slv();
	return 0;
}
posted @ 2024-02-28 21:22  rlc202204  阅读(11)  评论(0编辑  收藏  举报