動態凸包就是可以支持動態插入點,維護凸包信息的一類問題
又考到了,又被炸飛了(然而其實弱的連凸包性質都看不出來
注意只能支持動態插入點,而不支持動態刪除和插入
不過刪除的話如果不強制在線反過來就是插入啊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
