CF70D(动态凸包)

CF70D(动态凸包)

给出q(<=1e5)个询问,每次在加上一个点,维护凸包,或者询问某个点是否在凸包内(在边上也算)。

听说可以用cdq做……但是并不会。我等蒟蒻只会用平衡树做。

首先,假设已经维护出了某个点按照极角排序的凸包,那么对于加入的一个点,我们首先要查询它是在凸包内还是凸包外(这个功能也可以用于题目中的查询)。O表示极角排序的原点,next表示极角排序的下一个点,pre则表示上一个点:图片

那么,如果p在凸包外,a\(\times\)b就是正数,若p在凸包内a\(\times\)b则是非正整数。

接着,我们要维护凸包。维护凸包依然要查询next和pre:

图片

类似于gramham,不断通过删除next和pre维护凸包凸性。形象理解一下,可以看成p点伸出了两个筷子,不停尝试夹住凸包(凸包:喵喵喵?)

注意判断点是否在凸包内时,有个小坑点。O点必须选择在凸包内,既不能选择在凸包外也不能选择在凸包的边上。

#include <set>
#include <cmath>
#include <cstdio>
using namespace std;

typedef long long LL;
const LL maxn=1e5+5;
double ox=0, oy=0;
struct Point{
	LL op, x, y; double angle;
	Point (LL a=0, LL b=0):x(a), y(b){}
	void getangle(){ angle=atan2(y-oy, x-ox); }
}p[maxn];
Point operator +(const Point &a, const Point &b){ return Point(a.x+b.x, a.y+b.y); }
Point operator -(const Point &a, const Point &b){ return Point(a.x-b.x, a.y-b.y); }
LL operator *(const Point &a, const Point &b){ return a.x*b.y-a.y*b.x; } 
bool operator <(const Point &a, const Point &b){  //a是否在b的逆时针处 
	return a.angle<b.angle;	}
LL q;
typedef multiset<Point>::iterator iter;
typedef Point Vector;
multiset<Point> s;

iter nxt(iter x){ return x==--s.end()?s.begin():++x; }
iter pre(iter x){ return x==s.begin()?--s.end():--x; }

bool in(iter x){  
	if (s.size()<3) return false;
	return (*nxt(x)-*x)*(*pre(x)-*x)<=0;  //叉积(是不是神仙操作) 
}

void add(Point &x){
	iter it=s.insert(x); 
	if (s.size()<=3) return;
	if (in(it)){ s.erase(it); return; }
	while (s.size()>3&&(*nxt(it)-*it)*(*nxt(nxt(it))-*nxt(it))<=0)  //神仙操作*2 
		s.erase(nxt(it));  //注意加上=0以后,要判断size以免遇到两个点的情况 
	while (s.size()>3&&(*pre(it)-*it)*(*pre(pre(it))-*pre(it))>=0)  //神仙操作*3
		s.erase(pre(it));
}

bool query(Point &x){
	iter it=s.insert(x); bool flag;
	if (in(it)) flag=true; else flag=false;
	s.erase(it); return flag;
}

int main(){
	scanf("%lld", &q);
	for (LL i=0; i<q; ++i)
		scanf("%lld%lld%lld", &p[i].op, &p[i].x, &p[i].y); 
	Point t=p[0]+p[1]+p[2]; ox=(double)t.x/3; oy=(double)t.y/3;  
	//这样确定原点,保证原点不在凸包的边上 
	//若不这样:例子:A(-1, 0)  B(1, 0) Q(4, 0) 
	for (LL i=0; i<q; ++i) p[i].getangle();  //确定极角
	for (LL i=0; i<q; ++i){
		if (p[i].op==1) add(p[i]); 
		else puts(query(p[i])?"YES":"NO");
	}
	return 0;
}
posted @ 2018-07-23 15:33  pechpo  阅读(251)  评论(0编辑  收藏  举报