Codeforces Round #773 (Div. 2) E 题解
Codeforces Round #773 (Div. 2) E
知识点 区间覆盖
题意
对于一排人, 医生可以做三种操作:
- \(l, r, 0\) 表示第 \(l\) 到 \(r\) 个人中没有病人。
- \(l, r, 1\) 表示第 \(1\) 到 \(r\) 个人中至少有一个病人生病。
- 询问第 \(j\) 个人有没有生病。
对于每一个操作3,可以确定生病输出 YES
, 可以确定不生病输出 NO
, 否则输出 N/A
。
题意简化
- \(a_{l} \lor a_{l+1} \lor \dots \lor a_{r-1} \lor a_{r} = 0\)
- \(a_{l} \lor a_{l+1} \lor \dots \lor a_{r-1} \lor a_{r} = 1\)
- 询问 \(a_j\) 的情况
分析
-
对于第\(j\)个人而言:
- 若被包含在第1种区间,则一定是不生病的。
- 若被包含在第2种区间,而且在此区间内,其余人都是不生病的(被第1区间包含),那么一定是生病的。
- 其余情况均为不确定。
-
显然 不生病的人可以简单用区间覆盖解决,但是怎么处理有病的情况呢?
对所有第2区间进行保存暴力判断显然是超时的,并且这些区间不具有可加性,这意味着我们几乎无法用快速的方法去维护这些区间。
但1区间具有可加性,切入点就变为去维护第1区间。我们可以得知 \(a_j\) 左边和右边最远的没有生病的人位置\((l,r)\),然后检查第2区间是否有被 \((l,r)\) 所覆盖的。 -
区间覆盖使用线段树维护
问题转化为给定若干个区间,检查是否存在区间 \((L, R)\) 覆盖 区间 \((l,r)\)。我们采用合并区间的方法减少时间复杂度,具体思想就是,对于具有相同左端点的区间,我们只维护其最大的右端点位置,这样一来,\(O(n^2)\) 的时间复杂度便降低为\(O(n)\)。而在检查时,我们扫描 \((l, r)\)的所有右端点得到最大值 \(\max(R_l ,R_r)\),判定 \(\max(R_l ,R_r) >= r\) 即可。总时间复杂度\(O(nlogn)\)
代码
// from :
#include<bits/stdc++.h>
using namespace std;
#define fastio cin.tie(0);cout.tie(0);ios::sync_with_stdio(false)
typedef pair<int, int> PII;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int dx[] = {0, 0, 1, -1}, dy[] = {1, -1, 0, 0};
void debug(int x = 0) {
cout << "--ok(" << x << ")" << endl;
}
void yes() {
cout << "YES" << "\n";
}
void no() {
cout << "NO" << "\n";
}
void nope() {
cout << "N/A" << "\n";
}
const int N = 1e6 + 5, M = 3e5 + 5, mod = 1e9 + 7;
/* cout << "\n";*/
int dat[N<<2], add[N<<2];
int n;
void build(int p, int l, int r) {
dat[p] = add[p] = n + 1;
if (l == r) return;
int mid = l + r >> 1;
build(p<<1, l, mid);
build(p<<1|1, mid + 1, r);
}
void change(int p, int l, int r, int x, int y) {
if (l == r) {
dat[p] = min(dat[p], y);
return ;
}
int mid = l + r >> 1;
if (x <= mid) change(p<<1, l, mid, x, y);
else change(p<<1|1, mid + 1, r, x, y);
dat[p] = min(dat[p<<1], dat[p<<1|1]);
}
int ask(int p, int l, int r, int L, int R) {
if (L <= l && R >= r) return dat[p];
int mid = l + r >> 1;
int ans = n + 1;
if (L <= mid) ans = min(ans, ask(p<<1, l, mid, L, R));
if (R > mid) ans = min(ans, ask(p<<1|1, mid + 1, r, L, R));
return ans;
}
set<int> s;
void work(int l, int r) {
for (auto it = s.lower_bound(l); it != s.end() && *it <= r; it = s.erase(it));
}
void solve() {
int w; cin >> n >> w;
build(1, 1, n);
for (int i = 1; i <= n; i++) s.insert(i);
while (w--) {
int op, l, r, x;
cin >> op;
if (op == 0) {
cin >> l >> r >> x;
if (x == 0) work(l, r);
else change(1, 1, n, l, r);
}
else {
cin >> x;
if (!s.count(x)) no();
else {
auto it = s.lower_bound(x);
int l = 1, r = n;
if (it != s.begin()) l = *(--it) + 1;
it = s.lower_bound(x); ++it;
if (it != s.end()) r = *(it) - 1;
if (ask(1, 1, n, l, r) <= r) yes();
else nope();
}
}
}
}
int main(){
fastio; cout << setprecision(10);
solve();
return 0;
}