Codeforces Round #773 (Div. 2) E 题解

Codeforces Round #773 (Div. 2) E

知识点 区间覆盖

题意

对于一排人, 医生可以做三种操作:

  1. \(l, r, 0\) 表示第 \(l\)\(r\) 个人中没有病人。
  2. \(l, r, 1\) 表示第 \(1\)\(r\) 个人中至少有一个病人生病。
  3. 询问第 \(j\) 个人有没有生病。

对于每一个操作3,可以确定生病输出 YES , 可以确定不生病输出 NO , 否则输出 N/A

题意简化

  1. \(a_{l} \lor a_{l+1} \lor \dots \lor a_{r-1} \lor a_{r} = 0\)
  2. \(a_{l} \lor a_{l+1} \lor \dots \lor a_{r-1} \lor a_{r} = 1\)
  3. 询问 \(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;
}
 
posted @ 2022-03-24 17:03  九尾妖渚  阅读(28)  评论(0)    收藏  举报