2019.02.22【CodeForces70】D.Professor's task(动态凸包)

传送门


解析:

目前我能找到的最早的动态凸包题目。

初始给出三个点,要求在 O ( log ⁡ n ) O(\log n) O(logn)的时间内支持以下操作:

1.插入一个点并维护当前点集的凸包

2.询问某一个点是否在当前凸包内。
(当然也可能是询问某些更加复杂的操作,如周长,面积等,不过这个是动态凸包的基础操作,而其他的只是凸包基础操作)。

可以分两种方法考虑维护:极角序和水平序。

1.极角序

极角序略微有点麻烦,需要先确定一个在凸包内的点,一般选择起始三点的重心为凸包中心。

然后我们按照凸包上点到中心向量的极角对点进行排序。用平衡树维护。

判断是否在凸包内只需要二分找到两个将该点的角度夹在中间的点,然后叉积判一下就行了。

插入需要先判断是否在凸包内,在凸包外的话还是先二分找到两个夹住它的点,注意插入后可能需要删除两侧的一些点。

平衡树直接用set就行了。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<20|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	inline int getint(){
		re char c;
		re bool f=0;
		while(!isdigit(c=gc()))if(c=='-')f=1;re int num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return f?-num:num;
	}
}
using namespace IO;

struct Point{
	int x,y;
	ll len;
	double rad;
	Point(cs int &_x=0,cs int &_y=0):x(_x),y(_y){}
	friend ll operator*(cs Point &a,cs Point &b){return (ll)a.x*b.y-(ll)a.y*b.x;}
	friend ll dot (cs Point &a,cs Point &b){return (ll)a.x*b.x+(ll)a.y*b.y;}
	friend Point operator-(cs Point &a,cs Point &b){return Point(a.x-b.x,a.y-b.y);}
	friend bool operator<(cs Point &a,cs Point &b){
		return a.rad==b.rad?a.len<b.len:a.rad<b.rad;
	}
}a[4],o;

set<Point> conv;
typedef set<Point>::iterator point;

inline point nxt(point p){
	++p;
	if(p==conv.end())p=conv.begin();
	return p;
}

inline point pre(point p){
	if(p==conv.begin())p=conv.end();
	return --p;
}

inline bool inside(cs Point &p){
	re point i=conv.lower_bound(p),j;
	if(i==conv.end())i=conv.begin();
	j=pre(i);
	if((p-*j)*(*i-*j)<=0)return true;
	else return false;
}

inline void ins(cs Point &p){
	if(inside(p))return ;
	conv.insert(p);
	re point cur=conv.find(p);
	re point i=pre(cur),j=pre(i);
	while((*i-*j)*(*cur-*j)<=0){
		conv.erase(i);
		i=j,j=pre(j);
	}
	i=nxt(cur),j=nxt(i);
	while((*i-*j)*(*cur-*j)>=0){
		conv.erase(i);
		i=j;
		j=nxt(j);
	}
}

int q;
Point p;
signed main(){
	q=getint()-3;
	for(int re i=1;i<=3;++i){
		getint(),a[i].x=getint(),a[i].y=getint();
		o.x+=a[i].x;
		o.y+=a[i].y;
		a[i].x*=3;
		a[i].y*=3;
	}
	for(int re i=1;i<=3;++i){
		a[i].len=dot(a[i]-o,a[i]-o);
		a[i].rad=atan2(a[i].y-o.y,a[i].x-o.x);
		conv.insert(a[i]);
	}
	while(q--){
		switch(getint()){
			case 1:{
				p.x=getint()*3,p.y=getint()*3;
				p.rad=atan2(p.y-o.y,p.x-o.x);
				p.len=dot(p-o,p-o);
				ins(p);
				break;
			}
			case 2:{
				p.x=getint()*3,p.y=getint()*3;
				p.rad=atan2(p.y-o.y,p.x-o.x);
				p.len=dot(p-o,p-o);
				puts(inside(p)?"YES":"NO");
				break;
			}
		}
	}
	return 0;
}

2.水平序

水平序需要两棵平衡树,以横坐标为关键字来分别维护上下凸壳。

判断和插入都和上面差不多,但是感觉好像一些,用的也多一些。

由于是以横坐标为关键字,所以可以用map来维护了

代码(set):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<20|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	inline int getint(){
		re char c;
		re bool f=0;
		while(!isdigit(c=gc()))if(c=='-')f=1;re int num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return f?-num:num;
	}
}
using namespace IO;

cs int INF=0x3f3f3f3f;
struct Point{
	int x,y;
	Point(cs int &_x,cs int &_y):x(_x),y(_y){}
	friend Point operator-(cs Point &a,cs Point &b){return Point(a.x-b.x,a.y-b.y);}
	friend ll operator*(cs Point &a,cs Point &b){return (ll)a.x*b.y-(ll)a.y*b.x;}
	friend bool operator<(cs Point &a,cs Point &b){return a.x<b.x||(a.x==b.x&&a.y<b.y);}
};

set<Point> up,down;

inline bool inside(cs set<Point> &conv,int x,int y){
	if(!conv.size())return false;
	if(x<conv.begin()->x||x>conv.rbegin()->x)return false;
	set<Point>::iterator it=conv.lower_bound(Point(x,-INF));
	if(it!=conv.end()&&it->x==x)return it->y<=y;
	set<Point>::iterator r=conv.lower_bound(Point(x,y)),l=r;
	--l;
	return (Point(x,y)-*l)*(*r-*l)<=0;
}

inline void ins(set<Point> &conv,int x,int y){
	if(inside(conv,x,y))return ;
	set<Point>::iterator it=conv.lower_bound(Point(x,-INF));
	if(it!=conv.end()&&it->x==x)conv.erase(it);
	conv.insert(Point(x,y));
	set<Point>::iterator cur=conv.lower_bound(Point(x,y));
	set<Point>::iterator re i,j;
	for(i=cur,--i,j=i,--j;i!=conv.begin()&&cur!=conv.begin();i=j,--j)
	if((*i-*cur)*(*j-*cur)>=0)conv.erase(i);
	else break;
	for(i=cur,++i,j=i,++j;i!=conv.end()&&j!=conv.end();i=j,++j)
	if((*i-*cur)*(*j-*cur)<=0)conv.erase(i);
	else break;
}

int q;
signed main(){
	q=getint();
	while(q--){
		switch(getint()){
			case 1:{
				int x=getint(),y=getint();
				ins(up,x,-y);
				ins(down,x,y);
				break;
			}
			case 2:{
				int x=getint(),y=getint();
				puts(inside(up,x,-y)&&inside(down,x,y)?"YES":"NO");
				break;
			}
		}
	}
	return 0;
}

代码(map):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<20|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	inline int getint(){
		re char c;
		re bool f=0;
		while(!isdigit(c=gc()))if(c=='-')f=1;re int num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return f?-num:num;
	}
} 
using namespace IO;

map<int,int> up,down;
typedef map<int,int>::iterator point;

inline pair<int,int> operator-(cs pair<int,int> &a,cs pair<int,int> &b){
	return make_pair(a.first-b.first,a.second-b.second);
}

inline ll operator*(cs pair<int,int> &a,cs pair<int,int> &b){
	return (ll)a.first*b.second-(ll)a.second*b.first;
}

inline bool inside(map<int,int> &conv,int x,int y){
	if(!conv.size())return false;
	if(x<conv.begin()->first||x>conv.rbegin()->first)return false;
	if(conv.count(x))return conv[x]<=y;
	conv[x]=y;
	map<int,int>::iterator cur=conv.lower_bound(x),i=cur,j=cur;
	++j,--i;
	bool res=(*cur-*i)*(*j-*i)<=0;
	conv.erase(x);
	return res;
}

inline void ins(map<int,int> &conv,int x,int y){
	if(inside(conv,x,y))return ;
	conv[x]=y;
	map<int,int>::iterator cur=conv.lower_bound(x);
	map<int,int>::iterator re i,j;
	for(i=cur,--i,j=i,--j;i!=conv.begin()&&cur!=conv.begin();i=j--)
	if((*i-*cur)*(*j-*cur)>=0)conv.erase(i);
	else break;
	for(i=cur,++i,j=i,++j;i!=conv.end()&&j!=conv.end();i=j++)
	if((*i-*cur)*(*j-*cur)<=0)conv.erase(i);
	else break;
}

int q;
signed main(){
	q=getint();
	while(q--){
		switch(getint()){
			case 1:{
				int x=getint(),y=getint();
				ins(up,x,-y);
				ins(down,x,y);
				break;
			}
			case 2:{
				int x=getint(),y=getint();
				puts(inside(up,x,-y)&&inside(down,x,y)?"YES":"NO");
				break;
			}
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值