zoukankan      html  css  js  c++  java
  • CF70D(动态凸包)

    CF70D(动态凸包)

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

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

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

    那么,如果p在凸包外,a( imes)b就是正数,若p在凸包内a( imes)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;
    }
    
  • 相关阅读:
    BZOJ 1040 (ZJOI 2008) 骑士
    BZOJ 1037 (ZJOI 2008) 生日聚会
    ZJOI 2006 物流运输 bzoj1003
    ZJOI 2006 物流运输 bzoj1003
    NOI2001 炮兵阵地 洛谷2704
    NOI2001 炮兵阵地 洛谷2704
    JLOI 2013 卡牌游戏 bzoj3191
    JLOI 2013 卡牌游戏 bzoj3191
    Noip 2012 day2t1 同余方程
    bzoj 1191 [HNOI2006]超级英雄Hero——二分图匹配
  • 原文地址:https://www.cnblogs.com/MyNameIsPc/p/9354914.html
Copyright © 2011-2022 走看看