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;
    }
    
  • 相关阅读:
    每周问问你的团队这10个问题
    Android用java代码转换dp或者sp到px
    获取ScrollView的可见高度,和获取HorizontalScrollView的可见宽度
    关于自定义ViewGroup在ScrollView中无法显示的问题.
    Android源码混淆脚本proguard
    关于Bitmap内存溢出问题
    WebView退出时停止视频播放
    WebView页面加载完成后报空指针异常
    最近一次Android源码编译过程
    警告:Not targeting the latest versions of Android; compatibility modes apply. Consider testing and updating this version. Consult the ...
  • 原文地址:https://www.cnblogs.com/MyNameIsPc/p/9354914.html
Copyright © 2011-2022 走看看