codeforces 1641C Anonymity Is Important (思维好题,STL)

题意:
有 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值