传送门
解析:
目前我能找到的最早的动态凸包题目。
初始给出三个点,要求在 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;
}