1. 程式人生 > 其它 >Codeforces Round #773 (Div. 2) E 題解

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區間進行儲存暴力判斷顯然是超時的,並且這些區間不具有可加性,這意味著我們幾乎無法用快速的方法去維護這些區間。
但一區間具有可加性,切入點就變為去維護第一區間。我們可以得知 \(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(nlog_n)\)

程式碼

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