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;
}