动态凸包 学习总结

动态凸包就是可以支持动态插入点,维护凸包信息的一类问题

又考到了,又被炸飞了(然而其实弱的连凸包性质都看不出来

注意只能支持动态插入点,而不支持动态删除和插入

不过删除的话如果不强制在线反过来就是插入啊OwO

 

不是很喜欢水平序的动态凸包,因为要维护上下两个凸壳好烦

所以就学了一发极角序

大概做法是以极角序为键值用平衡树维护凸包上的点

每次插入的时候找到插入点的前驱后继,用叉积判断是否在内部

如果不在就插入,插入之后不断的判断插入后前驱是否在凸包内和后继是否在凸包内

并且不断的删除直至不能删除为止

因为只需要支持插入,删除,求前驱,求后继,所以直接用set就可以维护了

需要注意的几个地方:

1、一开始取的基准点必须要在凸包内部,因为只支持插入所以凸包可能变大,所以找一开始三个点的内部就可以了

2、排序的时候极角序相同按到基准点的长度排序

3、凸包是环状的,所以前驱和后继也是环状的,判一下就可以了

 

BZOJ 2300 防线修建

真是一道好的模板题目啊OwO

没有下凸壳,没有边界问题,一开始还给三个点

然而问题是写完这道题目写其他题目会被各种边界炸飞

题目显然是要求动态删除一个点,查询凸包周长

时间倒流一下就是动态凸包的裸题了,至于周长什么的插入删除维护一下就可以了

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<set>
#include<cmath>
#define eps 1e-10
using namespace std;
 
const int maxn=100010;
int n,x,y,m,q;
bool vis[maxn];
double ans=0;
struct Point{
    double x,y,ang,len;
    Point(double x=0,double y=0,double ang=0,double len=0):x(x),y(y),ang(ang),len(len){}
    void read(){scanf("%lf%lf",&x,&y);}
}a[maxn],o;
typedef Point Vector;
bool operator <(const Point &A,const Point &B){
    if(fabs(A.ang-B.ang)<eps)return A.len<B.len;
    return A.ang<B.ang;
}
Vector operator -(const Point &A,const Point &B){return Vector(A.x-B.x,A.y-B.y);}
double Cross(const Point &A,const Point &B){return A.x*B.y-A.y*B.x;}
double Dot(const Point &A,const Point &B){return A.x*B.x+A.y*B.y;}
double Get_len(const Point &A){return sqrt(Dot(A,A));}
double sqr(double x){return x*x;}
struct ASK{
    int type,u;
}Q[maxn];
int top=0;
double Ans[maxn];
set<Point>S;
set<Point>::iterator it,pre,tmp;
 
set<Point>::iterator Get_pre(set<Point>::iterator it){
    if(it==S.begin())it=S.end();
    it--;
    return it;
}
set<Point>::iterator Get_suf(set<Point>::iterator it){
    it++;
    if(it==S.end())it=S.begin();
    return it;
}
void Get_insert(const Point &p){
    it=S.lower_bound(p);
    if(it==S.end())it=S.begin();
    pre=Get_pre(it);
    if(Cross((*it)-(*pre),p-(*pre))>=0)return;
    ans-=Get_len((*it)-(*pre));
    ans+=Get_len(p-(*it))+Get_len(p-(*pre));
    S.insert(p);tmp=Get_pre(pre);
    while(Cross(p-(*pre),(*pre)-(*tmp))>=0){
        ans=ans-Get_len(p-(*pre))-Get_len((*pre)-(*tmp));
        ans=ans+Get_len(p-(*tmp));
        S.erase(pre);
        pre=tmp;tmp=Get_pre(pre);
    }tmp=Get_suf(it);
    while(Cross(p-(*it),(*it)-(*tmp))<=0){
        ans=ans-Get_len(p-(*it))-Get_len((*it)-(*tmp));
        ans=ans+Get_len(p-(*tmp));
        S.erase(it);
        it=tmp;tmp=Get_suf(it);
    }return;
}
 
int main(){
    scanf("%d%d%d",&n,&x,&y);
    a[0]=Point(0,0);a[1]=Point(n,0);a[2]=Point(x,y);
    ans+=Get_len(a[2]-a[0])+Get_len(a[2]-a[1]);
    for(int i=0;i<3;++i)o.x+=a[i].x,o.y+=a[i].y;
    o.x/=3;o.y/=3;
    for(int i=0;i<3;++i){
        a[i].ang=atan2(a[i].y-o.y,a[i].x-o.x);
        a[i].len=Get_len(a[i]-o);
        S.insert(a[i]);
    }scanf("%d",&m);
    for(int i=1;i<=m;++i){
        a[i].read();
        a[i].ang=atan2(a[i].y-o.y,a[i].x-o.x);
        a[i].len=Get_len(a[i]-o);
    }
    scanf("%d",&q);
    for(int i=1;i<=q;++i){
        scanf("%d",&Q[i].type);
        if(Q[i].type==1)scanf("%d",&Q[i].u),vis[Q[i].u]=true;
    }
    for(int i=1;i<=m;++i)if(!vis[i])Get_insert(a[i]);
    for(int i=q;i>=1;--i){
        if(Q[i].type==2)Ans[++top]=ans;
        else Get_insert(a[Q[i].u]);
    }
    for(int i=top;i>=1;--i)printf("%.2lf\n",Ans[i]);
    return 0;
}

codeforces 70D

这个是真动态凸包裸题了

注意边界问题就好啦OwO

代码貌似因为某些奇怪的原因找不到啦QAQ

跟上面几乎是一模一样的

 

OwO 自己计算几何太渣了

然而NOI会不会考呢?思考ing

posted @ 2016-07-06 08:10  _Vertical  阅读(3064)  评论(0编辑  收藏  举报