题解 P2521 [HAOI2011]防线修建
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;
}