题意:
有 n 个人和 q 个信息,
- 0 l r x,如果 x = 0,表示编号 l 到 r 的人没有病;否则,表示编号 l 到 r 的人至少有一个得病。
- 1 x,询问编号为 x 的人健康情况。
思路:
模拟即可。
首先, n 个人最初的健康情况都是未知的。
如何确定一个人的健康情况:一种方法是直接告知一个人没病;另一种方法 x 在至少一个人得病的区间内,并且只有 x 的情况未知,其他人都没得病。
假设知道 [3, 6] 这个区间内至少一个人得病,且知道 [2, 4] 和 [6, 8] 没人得病,即 1 0 0 0 5 0 0 0 9 10,可以确定 5 一定是得病的人。
显然,不断缩小至少有一个人得病的区间就可以比较优的找到谁得病了。
比如,同时知道 [3, 10] 和 [4, 6] 两个区间内至少有一个人得病,这时是无法确定 [3, 4] 和 [8, 10] 这两个区间内的情况,但 [4, 6] 内至少有一个人得病。
综上,需要维护两个信息:不能确定健康情况的人和至少有一人得病的区间。
不能确定健康情况的人涉及到动态删除(删除没得病的人),使用 set 维护(二分找人,erase删除编号)。set 的 erase 函数的时间复杂度是摊销常数,而 vector的时间复杂度是 O(n)。
至少有一人得病的区间涉及到区间嵌套问题,使用 map 维护。键作为左端点,值作为右端点。map 内部按照键排序,即按照左端点排序。比较相邻区间的右端点就可以得到嵌套关系。
具体操作看代码解释。
ACcode
#include <bits/stdc++.h>
#define ll long long
#define endl "\n"
const double eps = 1e-8;
const ll INF=0x3f3f3f3f;
const ll mod=998244353;
const int maxn = 2e5 + 5;
using namespace std;
#define PI acos(-1)
#define ld long double
#define IOS cin.tie(0), ios::sync_with_stdio(false)
set<int> v;
map<int, int> s;
int main(){
IOS;
int n, q;
cin >> n >> q;
// 初始不确定健康情况的人
for(int i = 0; i <= n+1; i++) v.insert(i);
int op, l, r, x;
while(q--){
cin >> op;
if(op == 0){
cin >> l >> r >> x;
if(x == 0){
while(1){ // 删除 [l, r] 之间的人
auto it = v.lower_bound(l);
if(*it > r) break;
v.erase(it);
}
}
else{
// 如果存在区间 a,b 被区间 l,r 包含,则不用进行操作
auto it = s.lower_bound(l);
if(it != s.end() && it->second <= r) continue;
// 否则,缩小区间
s[l] = r;
// 查询前一个区间是否包含当前区间,如果包含则删除前一个区间
it = s.find(l);
while(it != s.begin() && r <= prev(it)->second) s.erase(prev(it));
}
}
else{
cin >> x;
if(!v.count(x)) cout << "NO" << endl; // 被删除说明没病
else{
// 如果 x 在至少一个人得病的区间内,并且只有 x 的情况未知。
int l = *prev(v.find(x));
int r = *next(v.find(x));
auto it = s.upper_bound(l);
if(it != s.end() && it->first <= x && it->second < r) cout << "YES" << endl;
else cout << "N/A" << endl;
}
}
}
return 0;
}