題解 P2521 [HAOI2011]防線修建
阿新 • • 發佈:2021-06-20
Solution
實際上不需要使用set或平衡樹來維護凸包 , 使用連結串列即可 .
我們記錄每個點在整張圖上的前點和後點和在凸包上的前點和後點 , 對於刪除操作我們在連結串列上刪除這個點 , 如果它在凸包上 , 那麼重構它在凸包上的前後之間的區域並動態維護凸包周長 .
Code
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> using namespace std; int read() { int ret=0;char c=getchar(); while(c>'9'||c<'0')c=getchar(); while(c>='0'&&c<='9')ret=(ret<<3)+(ret<<1)+(c^48),c=getchar(); return ret; } const int maxn=1e5+5; int n,m; struct point { int x,y;int ord; const bool operator <(const point &a)const{if(x!=a.x)return x<a.x;return y<a.y;} const point operator -(const point &a)const{return point{x-a.x,y-a.y};} const int operator *(const point &a)const{return x*a.y-a.x*y;} friend double dis(point &a,point &b){return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));} }p[maxn]; int pre[maxn],nxt[maxn]; int cpre[maxn],cnxt[maxn]; bool used[maxn]; int sta[maxn],top; double ans; int num[maxn]; void andrew(int l,int r) { top=0;sta[++top]=l; for(int i=nxt[l];i<=r;i=nxt[i]) { while(top>=2&&(p[sta[top]]-p[sta[top-1]])*(p[i]-p[sta[top]])>=0)used[sta[top--]]=0; used[i]=1; sta[++top]=i; } for(int i=1;i<top;i++)ans+=dis(p[sta[i]],p[sta[i+1]]); for(int i=1;i<=top;i++) { if(i!=1)cpre[sta[i]]=sta[i-1]; if(i!=top)cnxt[sta[i]]=sta[i+1]; } } int main() { p[2].x=read();p[3].x=read();p[3].y=read(); n=read()+3; for(int i=4;i<=n;i++)p[i].x=read(),p[i].y=read(),p[i].ord=i-3; sort(p+1,p+n+1); for(int i=1;i<=n;i++)num[p[i].ord]=i; for(int i=1;i<=n;i++)pre[i]=i-1,nxt[i]=i+1; andrew(1,n); m=read(); while(m--) { int opt=read(); if(opt==1) { int x=read(); x=num[x]; pre[nxt[x]]=pre[x]; nxt[pre[x]]=nxt[x]; if(!used[x])continue; ans-=dis(p[cpre[x]],p[x])+dis(p[cnxt[x]],p[x]); andrew(cpre[x],cnxt[x]); } else if(opt==2)printf("%.2lf\n",ans); } return 0; }