2020牛客暑期多校训练营(第二场)H Happy Triangle 线段树维护相邻两个元素的差

思路比较简单,这题主要考验码力。

显然:分情况讨论:

1: a<b<x 时, ab尽量大

2:a<x<b时,a尽量大,b尽量小

3: x<a<b时,  b-a尽量小

1.2直接用set维护即可。

3用线段树维护,每次单点更新最多修改线段树2个点的值。

主要是细节问题。具体看代码。

 

#include <bits/stdc++.h>
using namespace std;
#define ls (o<<1)
#define rs (o<<1|1)
const int M = 2e5+7,inf = 2e9+7;
int tr[M<<2],li[M],sz;multiset<int>s;
void bd(int o,int l,int r)//初始化建树 
{
	tr[o]=inf;if(l==r)return ;
	int m=(l+r)/2;bd(ls,l,m);bd(rs,m+1,r);
}
void up(int o,int l,int r,int x,int d)//单点赋值
{
	if(l==r)
	{
		tr[o]=d;
		return ;
	}
	int m=(l+r)/2;
	if(x<=m)up(ls,l,m,x,d);
	else up(rs,m+1,r,x,d);
	tr[o]=min(tr[ls],tr[rs]);
}
int qu(int o,int l,int r,int x,int y)//区间查询最小值
{
	if(x<=l&&r<=y)return tr[o];
	int m=(l+r)/2,mn=inf;
	if(x<=m)mn=min(mn,qu(ls,l,m,x,y));
	if(y>m)mn=min(mn,qu(rs,m+1,r,x,y));
	return mn;
}
struct node{int op,x;}p[M];
int hs(int x){return lower_bound(li+1,li+1+sz,x)-li;}
int main()
{
//	freopen("8.in","r",stdin);
  	int Q;
  	scanf("%d",&Q);
  	for(int i=1;i<=Q;i++)
  	{
  		scanf("%d%d",&p[i].op,&p[i].x);
  		li[++sz]=p[i].x;
	}
	sort(li+1,li+1+sz);
	sz=unique(li+1,li+1+sz)-(li+1);
	//线段树维护1 ~ sz-1   表示第i+1个数与第i个数的差值 
	bd(1,1,sz);
	for(int i=1;i<=Q;i++)
	{
		int op=p[i].op,x=p[i].x;
		x=hs(x);
		auto nxt=s.upper_bound(x),pre=s.lower_bound(x);
		if(op==1)
		{
			if(s.count(x)==0)//每次插入最多只会改变x和 x的前驱。  因为线段树维护的是每个值与其后继的差值 
			{
				if(nxt!=s.end())up(1,1,sz,x,li[*nxt]-li[x]);//更新x 
				else up(1,1,sz,x,inf);
				if(pre!=s.begin())
				{
					--pre;
					if(s.count(*pre)==1)up(1,1,sz,*pre,li[x]-li[*pre]);//更新x得前驱 
				}
			}
			else up(1,1,sz,x,0);//权值x的数大于1,则只把权值x的后继减去x的权值更新为0,其余不变(因为我们要尽量求得权值差最小) 
			s.insert(x);
		}
		else if(op==2)
		{
			if(s.count(x)==1)//这种情况下最多改变x与其前驱。 
			{
				up(1,1,sz,x,inf);//更新x ,x不存在,直接设为inf 
				if(pre!=s.begin())
				{
					--pre;
					if(s.count(*pre)==1)//大于1始终为0 
					{
						if(nxt!=s.end())up(1,1,sz,*pre,li[*nxt]-li[*pre]);//更新x得前驱
						else up(1,1,sz,*pre,inf);//x的前驱的后继不存在,其线段树维护的值直接设为inf 
					}
				}
			}
			else if(s.count(x)==2)//这种情况下,只会改变x的值,(如果x后继存在的话) 
			{
				if(nxt!=s.end())up(1,1,sz,x,li[*nxt]-li[x]);
				else up(1,1,sz,x,inf);
			}
			//其余情况不会改变 
			s.erase(s.lower_bound(x));
		}
		else
		{
		//	for(auto x:s)cout<<li[x]<<" ";
	//		cout<<endl;
			bool flag=false;
			if(pre!=s.begin())
			{
				--pre;auto pre2=pre;
				if(pre2!=s.begin())
				{
					--pre2;
					if(li[*pre]+li[*pre2]>li[x])flag=true;
				//	cout<<"----   "<<li[*pre]<<" "<<li[*pre2]<<" "<<li[x]<<endl;
				}
				if(nxt!=s.end()&&li[*pre]+li[x]>li[*nxt])flag=true;
			}
			int mn=qu(1,1,sz,x,sz);
			if(mn<li[x])flag=true;
		//	cout<<mn<<"  "<<x<<endl;
			if(flag)puts("Yes");
			else puts("No");
		}
	}
	return 0;
}
/*
20
1 902364824
1 976541380
1 195794093
1 986278573
2 902364824
1 277734300
1 102939109
1 897242342
1 735343743
1 71002172
2 986278573
1 493686892
1 979151197
1 715139216
3 7496641
1 785130190
2 897242342
3 7535169
3 9808246
3 79704

*/

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值