[bzoj2300] [HAOI2011]防线修建

Description

近来A国和B国的矛盾激化,为了预防不测,A国准备修建一条长长的防线,当然修建防线的话,肯定要把需要保护的城市修在防线内部了。可是A国上层现在还犹豫不决,到底该把哪些城市作为保护对象呢?又由于A国的经费有限,所以希望你能帮忙完成如下的一个任务:

1.给出你所有的A国城市坐标

2.A国上层经过讨论,考虑到经济问题,决定取消对i城市的保护,也就是说i城市不需要在防线内了

3.A国上层询问对于剩下要保护的城市,修建防线的总经费最少是多少

你需要对每次询问作出回答。注意单位1长度的防线花费为1。

A国的地形是这样的,形如下图,x轴是一条河流,相当于一条天然防线,不需要你再修建

A国总是有两个城市在河边,一个点是(0,0),一个点是(n,0),其余所有点的横坐标均大于0小于n,纵坐标均大于0。A国有一个不在(0,0)和(n,0)的首都。(0,0),(n,0)和首都这三个城市是一定需要保护的。

img

上图中,A,B,C,D,E点为A国城市,且目前都要保护,那么修建的防线就会是A-B-C-D,花费也就是线段AB的长度+线段BC的长度+线段CD的长度,如果,这个时候撤销B点的保护,那么防线变成下图

img

Input

第一行,三个整数n,x,y分别表示河边城市和首都是(0,0),(n,0),(x,y)。

第二行,一个整数m。

接下来m行,每行两个整数a,b表示A国的一个非首都非河边城市的坐标为(a,b)。

再接下来一个整数q,表示修改和询问总数。

接下来q行每行要么形如1 i,要么形如2,分别表示撤销第i个城市的保护和询问。

Output

对于每个询问输出1行,一个实数v,表示修建防线的花费,保留两位小数

Sample Input

4 2 1
2
1 2
3 2
5
2
1 1
2
1 2
2

Sample Output

6.47
5.84
4.47

Solution

考虑把操作顺序改一下,我们反着做,就变成了维护凸包,支持加点。

由于是个上凸壳,可以直接用一个\(set\)维护凸包上的点的\(x\)坐标。

然后每次加点\(lower\_bound\)找到\(x\)最近的一条边,然后判一下在不在凸包内。

若不在凸包内,就往两边暴力判满不满足上凸壳性质,若不满足,就删点。

每个点只会被至多加一次,删一次,时间复杂度\(O(n\log n)\)

#include<bits/stdc++.h>
using namespace std;
 
void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
  
void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
 
const int maxn = 2e6+10;
 
struct point {
    int x,y;
    int operator ^ (const point &rhs) const {return x*rhs.y-rhs.x*y;}
    int operator < (const point &rhs) const {return x<rhs.x||(x==rhs.x&&y<rhs.y);}
    point operator - (const point &rhs) const {return (point){x-rhs.x,y-rhs.y};}
}a[maxn],sta[maxn],r[maxn];
 
int cnt,op[maxn],w[maxn],vis[maxn],top,m;
 
double ans;
 
set<point > s;
 
#define sqr(x) ((x)*(x))
double dis(point x,point y) {return sqrt(sqr(x.x-y.x)+sqr(y.y-x.y));}
 
void make() {
    int num=0;
    for(int i=1;i<=cnt;i++) if(!vis[i]) r[++num]=a[i];
    sort(r+1,r+num+1);sta[++top]=r[1],sta[++top]=r[2];
    for(int i=3;i<=num;i++) {
        while(top>1&&((r[i]-sta[top-1])^(sta[top]-sta[top-1]))<=0) top--;
        sta[++top]=r[i];
    }
    s.insert(sta[1]);
    for(int i=2;i<=top;i++) s.insert(sta[i]),ans+=dis(sta[i],sta[i-1]);
}
 
double res[maxn+8];
int Top;
 
void solve() {
    for(int i=m;i;i--) {
        if(op[i]==2) res[++Top]=ans;
        else {
            point x=a[w[i]+3];
            set <point > :: iterator it,it2,pre,nxt;
            it=s.lower_bound(x);it--;
            it2=it;it2++;
            if(((x-(*it))^((*it2)-(*it)))>=0) continue;
            ans-=dis(*it,*it2);
            while(it!=s.begin()) {
                pre=it;pre--;
                if(((x-(*it))^((*it)-(*pre)))>0) break;
                ans-=dis(*it,*pre);
                it--;pre++;s.erase(pre);
            }
            while(it2!=s.end()) {
                nxt=it2;nxt++;
                if((((*it2)-x)^((*nxt)-(*it2)))<0) break;
                ans-=dis(*it2,*nxt);
                nxt--;it2++;s.erase(nxt);
            }
            it=s.lower_bound(x);
            ans+=dis(x,*it);
            it--,ans+=dis(x,*it);
            s.insert(x);
        }
    }
    for(int i=Top;i;i--) printf("%.2lf\n",res[i]);
}
 
int main() {
    int n,x,y;read(n),read(x),read(y);
    a[++cnt]=(point){0,0};
    a[++cnt]=(point){n,0};
    a[++cnt]=(point){x,y};
    read(n);
    for(int i=1;i<=n;i++) read(x),read(y),a[++cnt]=(point){x,y};
    read(m);
    for(int i=1;i<=m;i++) {
        read(op[i]);if(op[i]==1) read(w[i]),vis[w[i]+3]=1;
    }
    make();solve();
    return 0;
}
posted @ 2019-01-26 16:39  Hyscere  阅读(230)  评论(0编辑  收藏  举报