CF1883D In Love

发布时间 2023-10-27 10:45:40作者: One_JuRuo

思路

如果每一次加或者删一个区间,再去暴力找有没有互不相交的区间的话,铁定 TLE。

那么,我们考虑维护有多少对互不相交的区间,那么每次加或者删一个区间,就去算这个区间对答案的贡献,然后再看答案是否为 \(0\) 即可快速判断有没有互不相交的区间。

现在考虑如何计算一个新加入或者删去的区间能让互不相交的区间对数变化多少呢?

加入新加入或者删去的区间是 \([l,r]\),那么加入和删去带来的答案变化都应该相同,只是一个是加,一个是减,所以我们没必要分开讨论。

如果两个区间不相交,必须满足其中一个区间的左端点大于另一个区间的右端点,那么对于区间 \([l,r]\),要满足条件,就必须要左端点大于 \(r\),右端点小于 \(l\)

但是,如果还是全部扫一遍,挨个判断能否对答案造成贡献的话,复杂度高达 \(O(q^2)\),依然会 TLE,所以考虑用个数据结构维护区间加,而每次加或者减一个区间就是单点修改,树状数组和线段树都可以完成,我选择了线段树。

无论是树状数组,还是线段树,就和值域有关系了,观察到值域达到了 \(10^9\),直接做肯定不行,所以还需要离散化。

AC code

#include<bits/stdc++.h>
using namespace std;
int n,a[200005],num,ans;
struct opti{int l,r;char ch[2];}b[200005];
struct node{int l,r,s[2];}t[800005];
inline void pushup(int p){for(int i=0;i<2;++i) t[p].s[i]=t[p<<1].s[i]+t[p<<1|1].s[i];}
void build(int p,int l,int r)
{
	t[p].l=l,t[p].r=r;
	if(l==r) return;
	int mid=l+r>>1;
	build(p<<1,l,mid),build(p<<1|1,mid+1,r);
}
void update(int p,int x,int op,int k)
{
	if(t[p].l==t[p].r){t[p].s[op]+=k;return;}
	int mid=t[p].l+t[p].r>>1;
	if(mid>=x) update(p<<1,x,op,k);
	else update(p<<1|1,x,op,k);
	pushup(p);
}
int ask(int p,int l,int r,int k)
{
	if(r<l) return 0;//因为出现了+1和-1,所以可能区间不存在,需要特判
	if(t[p].l>=l&&t[p].r<=r) return t[p].s[k];
	int mid=t[p].l+t[p].r>>1,ans=0;
	if(mid>=l) ans=ask(p<<1,l,r,k);
	if(mid<r) ans+=ask(p<<1|1,l,r,k);
	return ans;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;++i) scanf("%s%d%d",b[i].ch,&b[i].l,&b[i].r),a[i*2-1]=b[i].l,a[i*2]=b[i].r;
	sort(a+1,a+2*n+1),num=unique(a+1,a+2*n+1)-a-1;
	build(1,1,num);
	for(int i=1;i<=n;++i)
	{	
		b[i].l=lower_bound(a+1,a+num+1,b[i].l)-a,b[i].r=lower_bound(a+1,a+num+1,b[i].r)-a;
		if(b[i].ch[0]=='+')
		{
			ans+=ask(1,b[i].r+1,num,0)+ask(1,1,b[i].l-1,1),update(1,b[i].l,0,1),update(1,b[i].r,1,1);
			if(ans) printf("YES\n");
			else printf("NO\n");
		}
		else
		{
			ans-=ask(1,b[i].r+1,num,0)+ask(1,1,b[i].l-1,1),update(1,b[i].l,0,-1),update(1,b[i].r,1,-1);
			if(ans) printf("YES\n");
			else printf("NO\n");
		}
	}
	return 0;
}