【2020牛客多校第二场】Happy Triangle(数据结构)

题意

维护一个可重集合 S S S,要求支持一下操作:

  • 插入一个数 x x x
  • 删除一个数 x x x
  • 对于给定的数 x x x,查询是否存在 a , b ∈ S a,b∈S a,bS,使得 x , a , b x,a,b x,a,b 作为边长能构成一个非退化三角形。

题解

查询时,假设得到一组可行解 a , b ( a ≤ b ) a,b (a\le b) a,b(ab),那么如果将a替换为b的前驱,也一定是一组可行解(可由三角形三边关系得出)。那么判断是否有解可以只查找集合中相邻的边,判断边长和是否大于 x x x 且差小于 x x x 即可。可以记录一个数与它的前驱之差 c c c,找到第一个与其前驱之和大于 x x x 的数,查询它之后 c c c 的最小值是否小于 x x x 即可。
找到第一个与其前驱之和大于 x x x 的数,可以找到第一个大于 x / 2 x/2 x/2 的数,看它与前驱之和是否大于 x x x,若不是,则其后继就是我们要找的数。

由此,我们只需维护一个数据结构,支持:

  • 插入一个数并保证有序
  • 删除一个数
  • 查找一个数的前驱和后继(由于插入时可能影响其后继与前驱的差值)
  • 单点修改、区间查询最大值

此时我们有几种方案:平衡树、动态开点的权值线段树、权值线段树+离散化。我采用的是动态开点的线段树(维护最大值)和map(维护集合),相对还是比较好写的。
(好久没写过这么长的代码了,还好没出很多错……)

#include
using namespace std;

struct node{
	int l,r,m;
	node *ls,*rs;
	node(int l,int r,int m=2e9):l(l),r(r),m(m){ls=rs=0;}
	void upd(){
		m=2e9;
		if(ls)m=ls->m;
		if(rs)m=min(m,rs->m);
	}
};
node *rt=new node(0,1e9);

int ask_min(int l,int r,node *p=rt){
	if(!p || l>r)return 2e9;
	if(l<=p->l && r>=p->r)return p->m;
	int m=(p->l+p->r)/2,ans=2e9;
	if(l<=m)ans=min(ans,ask_min(l,r,p->ls));
	if(r>m)ans=min(ans,ask_min(l,r,p->rs));
	return ans;
}
void change(int x,int mi,node *p=rt){
	if(p->l==p->r){
		p->m=mi;
		return;
	}
	int m=(p->l+p->r)/2;
	if(x<=m){
		if(!p->ls)p->ls=new node(p->l,m);
		change(x,mi,p->ls);
	}
	else {
		if(!p->rs)p->rs=new node(m+1,p->r);
		change(x,mi,p->rs);
	}
	p->upd();
}
map<int,int>mp;
void add(int x){
	mp[x]++;
	if(mp[x]==1){
		auto it=mp.lower_bound(x);
		++it;
		if(it!=mp.end() && it->second==1)
			change(it->first,it->first-x);
		--it;
		int l=-1e9;
		if(it!=mp.begin())l=(--it)->first;
		change(x,x-l);
	}
	else if(mp[x]==2)change(x,0);
}
void del(int x){
	auto it=mp.lower_bound(x);
	mp[x]--;
	int l=-1e9;
	if(it!=mp.begin()){
		l=(--it)->first;
		++it;
	}
	if(mp[x]==0){
		if((++it)!=mp.end() && it->second==1)
			change(it->first,it->first-l);
		change(x,2e9);
		mp.erase(x);
	}
	else if(mp[x]==1)change(x,x-l);
}
int ask(int x){
	auto it=mp.lower_bound(x/2+1);
	if(it==mp.end())return 2e9;
	if(it->second>1)return it->first;
	auto itp=it;
	if(it!=mp.begin()){
		--itp;
		if(it->first+itp->first>x)return it->first;
	}
	if((++it)!=mp.end())return it->first;
	return 2e9;
}
int main(){
	ios::sync_with_stdio(0);
	int n;
	cin>>n;
	for(int i=0,op,x;i<n;i++){
		cin>>op>>x;
		if(op==1)add(x);
		if(op==2)del(x);
		if(op==3){
			if(ask_min(ask(x),1e9)<x)cout<<"Yes\n";
			else cout<<"No\n";
		}
	}
	return 0;
}

你可能感兴趣的:(数据结构,数据结构#线段树)