动态凸包 学习总结
动态凸包就是可以支持动态插入点,维护凸包信息的一类问题
又考到了,又被炸飞了(然而其实弱的连凸包性质都看不出来
注意只能支持动态插入点,而不支持动态删除和插入
不过删除的话如果不强制在线反过来就是插入啊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