上图中,A,B,C,D,E点为A国城市,且目前都要保护,那么修建的防线就会是A-B-C-D,花费也就是线段AB的长度+线段BC的长度+线段CD的长度,如果,这个时候撤销B点的保护,那么防线变成下图
对于每个询问输出1行,一个实数v,表示修建防线的花费,保留两位小数
题解:splay动态维护凸包。
因为删点不好操作,所以我们把数据离线,然后倒序处理,把删点操作变成加点操作。
用splay维护凸包中的点横坐标有序,插入一个点,如果(x,pre(x))的斜率小于(x,next(x))的斜率的话(注意特判横坐标相等的情况),说明形成的了一个凹陷的角,不满足凸包,直接删去刚插入的点即可。
否则,我们需要从这个点的左边找到第一条斜率(x,pre(x))<斜率(pre(x),pre(pre(x)))的边,不断的删去中间不满足的点,顺便更新答案。
从这个点的右边找到右边第一个斜率(x,next(x))>斜率(next(x),next(next(x)))的边,不断删除中间不满足的点。
需要注意的是在向左寻找的时候,如果两个点横坐标相同,斜率需要返回最大值,向右寻找的时候需要返回最小值。
#include
#include
#include
#include
#include
#define N 200003
#define inf 1000000000
using namespace std;
int fa[N],ch[N][3],xl[N],yl[N],n,m,pd[N],root;
double ans[N],ans1,ks[N],ks1[N];
struct point
{
double x,y;
}p[N];
struct data
{
int opt,x;
}a[N];
int get(int x)
{
return ch[fa[x]][1]==x;
}
void clear(int x)
{
fa[x]=ch[x][0]=ch[x][1]=0;
}
void rotate(int x)
{
int y=fa[x]; int z=fa[y]; int which=get(x);
ch[y][which]=ch[x][which^1]; fa[ch[x][which^1]]=y;
ch[x][which^1]=y; fa[y]=x;
if (z) ch[z][ch[z][1]==y]=x;
fa[x]=z;
}
void splay(int x,int tar)
{
for (int f;(f=fa[x])!=tar;rotate(x))
if (fa[f]!=tar)
rotate(get(x)==get(f)?f:x);
if (!tar) root=x;
}
void solve(double x,double y,int k)
{
if (!root)
{
root=k; fa[k]=ch[k][0]=ch[k][1]=0;
return;
}
int now=root,f;
while (true)
{
f=now;
now=ch[now][x>p[now].x];
if (!now)
{
fa[k]=f; ch[f][x>p[f].x]=k;
splay(k,0); return;
}
}
}
int pre(int now)
{
now=ch[now][0];
while (ch[now][1]) now=ch[now][1];
return now;
}
int next(int now)
{
now=ch[now][1];
while (ch[now][0]) now=ch[now][0];
return now;
}
void del(int now)
{
if (!ch[now][0]&&!ch[now][1])
{
clear(now); root=0; return;
}
if (!ch[now][0])
{
int ordr=now; now=ch[now][1]; fa[now]=0; clear(ordr); return;
}
if (!ch[now][1])
{
int ordr=now; now=ch[now][0]; fa[now]=0; clear(ordr); return;
}
int t=pre(now),oldr=root;
splay(t,0); fa[ch[oldr][1]]=root; ch[root][1]=ch[oldr][1]; clear(oldr);
}
double getdis(int x,int y)
{
int t=p[x].x-p[y].x;
int t1=p[y].y-p[x].y;
return sqrt(t*t+t1*t1);
}
double getk(int x,int y,int opt)
{
if (p[x].x==p[y].x)
if (opt==1) return inf;
else return -inf;
return (p[x].y-p[y].y)/(p[x].x-p[y].x);
}
void insert(double x,double y,int k)
{
solve(x,y,k);
int l=pre(k); int r=next(k);
if (!l||!r) return;
splay(l,k); splay(r,k);
if(p[l].x!=p[k].x&&p[k].x!=p[r].x&&getk(l,k,1)<=getk(k,r,1)||(p[k].x==p[l].x&&p[k].y<=p[l].y)||(p[k].x==p[r].x&&p[k].y<=p[r].y))
{
del(k);
return;
}
ans1-=getdis(l,r);
ks[k]=getk(l,k,1); ks[r]=getk(k,r,1);
ks1[k]=getk(k,r,2); ks1[l]=getk(l,k,2);
ans1+=getdis(l,k); ans1+=getdis(k,r);
int now=k;
while(ks[now]>=ks[l]&&l!=1)
{
ans1-=getdis(now,l); splay(l,0); ans1-=getdis(l,pre(l));
del(l); splay(now,0); l=pre(now);
//splay(l,now);
ks[now]=getk(l,now,1);
ks1[l]=getk(l,now,2);
ans1+=getdis(l,now);
}
while (ks1[now]<=ks1[r]&&r!=2)
{
ans1-=getdis(now,r); splay(r,0); ans1-=getdis(r,next(r));
del(r); splay(now,0); r=next(now);
//splay(r,now);
ks1[now]=getk(now,r,2);
ks[r]=getk(now,r,1);
ans1+=getdis(now,r);
}
}
int main()
{
freopen("a.in","r",stdin);
freopen("my.out","w",stdout);
int x1,y1;
scanf("%d%d%d",&n,&x1,&y1);
p[1].x=0; p[1].y=0; p[2].x=n; p[2].y=0; p[3].x=x1; p[3].y=y1;
insert(0,0,1);
insert(n,0,2); ans1+=getdis(1,2); ks[1]=inf; ks1[2]=-inf;
insert(x1,y1,3);
scanf("%d",&m);
for (int i=4;i<=m+3;i++)
scanf("%lf%lf",&p[i].x,&p[i].y);
int t; scanf("%d",&t);
for (int i=1;i<=t;i++)
{
scanf("%d",&a[i].opt);
if (a[i].opt==1) scanf("%d",&a[i].x),a[i].x+=3,pd[a[i].x]=1;
}
for (int i=4;i<=m+3;i++)
if (!pd[i]) insert(p[i].x,p[i].y,i);
int cnt=0;
for (int i=t;i>=1;i--)
{
if (a[i].opt==1) {
int k=a[i].x;
insert(p[k].x,p[k].y,k);
}
else ans[++cnt]=ans1;
}
for (int i=cnt;i>=1;i--)
printf("%0.2lf\n",ans[i]);
}