Problem
近来 A A A 国和 B B B 国的矛盾激化,为了预防不测, A A A 国准备修建一条长长的防线,当然修建防线的话,肯定要把需要保护的城市修在防线内部了。可是 A A A 国上层现在还犹豫不决,到底该把哪些城市作为保护对象呢?又由于A国的经费有限,所以希望你能帮忙完成如下的一个任务:
- 给出你所有的 A A A 国城市坐标。
- A A A 国上层经过讨论,考虑到经济问题,决定取消对 i i i 城市的保护,也就是说 i i i 城市不需要在防线内了。
- A A A 国上层询问对于剩下要保护的城市,修建防线的总经费最少是多少。
你需要对每次询问作出回答。注意单位 1 1 1 长度的防线花费为 1 1 1。
A A A 国的地形是这样的,形如下图, x x x 轴是一条河流,相当于一条天然防线,不需要你再修建。
A
A
A 国总是有两个城市在河边,一个点是
(
0
,
0
)
(0,0)
(0,0),一个点是
(
n
,
0
)
(n,0)
(n,0),其余所有点的横坐标均大于
0
0
0 小于
n
n
n,纵坐标均大于
0
0
0。A国有一个不在
(
0
,
0
)
(0,0)
(0,0) 和
(
n
,
0
)
(n,0)
(n,0) 的首都。
(
0
,
0
)
,
(
n
,
0
)
(0,0),(n,0)
(0,0),(n,0) 和首都这三个城市是一定需要保护的。
上图中,
A
,
B
,
C
,
D
,
E
A,B,C,D,E
A,B,C,D,E 点为
A
A
A 国城市,且目前都要保护,那么修建的防线就会是
A
−
B
−
C
−
D
A-B-C-D
A−B−C−D,花费也就是线段
A
B
AB
AB 的长度+线段
B
C
BC
BC 的长度+线段
C
D
CD
CD 的长度。如果,这个时候撤销
B
B
B 点的保护,那么防线变成下图
Solution
由于删点不好做,我们先把删除操作离线下来,然后反着加点。
现在的问题就是如何维护这个动态凸包。
我们先把求出每个点的极角,这样在后面就可以快速求出一个点的前驱和后继。
分两种情况讨论:
- 点 x x x 已经在凸包内,则直接 r e t u r n return return。
- 若点 x x x 不在凸包内,找到前驱 p r e pre pre 和后继 s u f suf suf,把 ( p r e , s u f ) (pre,suf) (pre,suf) 的边断掉,加入 ( x , p r e ) (x,pre) (x,pre), ( x , s u f ) (x,suf) (x,suf)。
要注意的是加入 x x x 可能会使 x , p r e , p r e − 1 x,pre,pre-1 x,pre,pre−1 之间的角大于 180 180 180( s u f suf suf 同理),要用 w h i l e while while 判断一下。
由于要快速查找前驱和后继,我们要用平衡树。(代码实现中用的 s e t set set。)
Code
#include<set>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define eps 1e-8
#define iterator set<point>::iterator
using namespace std;
bool Del[N];
double now,ans[N];
int n,x,y,m,q;
int Sign(double x) {return (x>eps)?1:((x<-eps)?-1:0);}
struct point{
double x,y,angle,len;
point(){}
point(double x,double y):x(x),y(y){}
friend point operator+(const point &p,const point &b) {return point(p.x+b.x,p.y+b.y);}
friend point operator-(const point &p,const point &b) {return point(p.x-b.x,p.y-b.y);}
friend double dot(const point &p,const point &b) {return p.x*b.x+p.y*b.y;}
friend double cross(const point &p,const point &b) {return p.x*b.y-p.y*b.x;}
friend double dis(const point &p) {return sqrt(p.x*p.x+p.y*p.y);}
}p[N];
struct query{int x,op;}Q[N];
bool operator<(const point &p,const point &q) {return Sign(p.angle-q.angle)==0?p.len<q.len:p.angle<q.angle;}
set<point>Set;
iterator pre,suf,temp;
iterator Prefix(iterator it){
if(it==Set.begin()) it=Set.end();
it--;
return it;
}
iterator Suffix(iterator it){
it++;
if(it==Set.end()) it=Set.begin();
return it;
}
void Insert(point x){
suf=Set.lower_bound(x);
if(suf==Set.end()) suf=Set.begin();
pre=Prefix(suf);
if(cross((*suf)-(*pre),x-(*pre))>=0) return;
now-=dis((*pre)-(*suf));
now+=dis(x-(*pre))+dis(x-(*suf));
Set.insert(x);
temp=Prefix(pre);
while(cross(x-(*pre),(*pre)-(*temp))>=0){
now+=dis(x-(*temp));
now-=dis((*pre)-(*temp))+dis(x-(*pre));
Set.erase(pre);
pre=temp,temp=Prefix(pre);
}
temp=Suffix(suf);
while(cross(x-(*suf),(*suf)-(*temp))<=0){
now+=dis(x-(*temp));
now-=dis((*suf)-(*temp))+dis(x-(*suf));
Set.erase(suf);
suf=temp,temp=Suffix(suf);
}
return;
}
int main(){
scanf("%d%d%d",&n,&x,&y);
p[0]=point(0,0),p[1]=point(n,0),p[2]=point(x,y);
now=dis(p[2]-p[0])+dis(p[2]-p[1]);
for(int i=0;i<=2;++i){
p[i].len=dis(p[i]);
p[i].angle=atan2(p[i].y,p[i].x);
Set.insert(p[i]);
}
scanf("%d",&m);
for(int i=1;i<=m;++i){
scanf("%lf%lf",&p[i].x,&p[i].y);
p[i].len=dis(p[i]),p[i].angle=atan2(p[i].y,p[i].x);
}
scanf("%d",&q);
for(int i=1;i<=q;++i){
scanf("%d",&Q[i].op);
if(Q[i].op==1) scanf("%d",&Q[i].x),Del[Q[i].x]=true;
}
for(int i=1;i<=m;++i)
if(!Del[i]) Insert(p[i]);
int tot=0;
for(int i=q;i>=1;--i){
if(Q[i].op==2) ans[++tot]=now;
else Insert(p[Q[i].x]);
}
for(int i=tot;i>=1;--i) printf("%.2lf\n",ans[i]);
return 0;
}