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;
}

转载于:https://www.cnblogs.com/MyNameIsPc/p/9354914.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值