Codeforces Round #773 (Div. 2) E 題解
阿新 • • 發佈:2022-03-24
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區間進行儲存暴力判斷顯然是超時的,並且這些區間不具有可加性,這意味著我們幾乎無法用快速的方法去維護這些區間。
但一區間具有可加性,切入點就變為去維護第一區間。我們可以得知 \(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; }