题意:
有 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#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 v; map 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; }



