bzoj 2300: [HAOI2011]防线修建

来源:互联网 发布:第三方数据公司 编辑:程序博客网 时间:2024/04/30 05:06

2300: [HAOI2011]防线修建

Time Limit: 10 Sec Memory Limit: 512 MB
Submit: 914 Solved: 502
[Submit][Status][Discuss]
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)和首都这三个城市是一定需要保护的。

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

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
HINT

m<=100000,q<=200000,n>1

所有点的坐标范围均在10000以内, 数据保证没有重点


【分析】
自己第一次做出来凸包还有set的题…感觉蛮不容易的(额大爷不要D,也不要嘲笑)

总的来说这是一道凸包好题(嘿嘿)

由于正着做比较麻烦,我们考虑反着想。一个点一个点的加进来构成凸包,这样比较容易处理。

  1. 首先我们把所有需要被盖住的点用graham算法构成一个凸包,将凸包的顶点集加入set中去。set是按x为关键字从小到大排序的。
  2. 然后逆着把点一个一个加进来,每加一个点就以x为关键字二分查找出它的左右两点。然后用叉积判断一下这个新点是否被原凸包覆盖,如果是,那么直接进行下一步,也就是重复2操作,如果不是,那么执行3操作
  3. 先把新点左右两点的距离减掉,然后左点向左循环,右点向右循环,用叉积判断用不用删除当前点,以左点为例,如果需要删除当前左点,就把左点与左点左边相邻的点之间的距离减去,直到不需要删除,构成一个新凸包,并将当前左点与新点的距离加入答案。右点也是这样。

不得不说set确实挺好用的…就是具体实现起来会有一些麻烦…不过比手写强吧= =

代码略长,不过不是很难懂,大家主要看思路。


【代码】

 //bzoj 2300: [HAOI2011]防线修建#include<algorithm>#include<iostream>#include<cstring>#include<cstdio>#include<cmath>#include<set>#define ll long long#define M(a) memset(a,0,sizeof a)#define fo(i,j,k) for(i=j;i<=k;i++)using namespace std;const int mxn=100005;int n,m,X,Y,top,tot;bool flag[mxn];int x[mxn],y[mxn],to[mxn],ask[mxn<<1],s[mxn];struct node{    int x,y,num;    bool operator < (node a) const    {        if(x!=a.x) return x<a.x;        else return y<a.y;    }}p[mxn];set <node> tree;double now,ans[mxn<<1];inline bool comp(const node &a,const node &b){    if(a.x!=b.x) return a.x<b.x;    return a.y<b.y;}inline int cross(node a,node b,node c)   //a->b向量与a->c向量的叉积(注意方向) {    return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y);}inline double dis(node a,node b)   //距离 {    double t1=(b.x-a.x)*(b.x-a.x),t2=(b.y-a.y)*(b.y-a.y);    return sqrt(t1+t2);}int main(){    int i,j,opt,q,tx,ty,ex,ey,wx,wy;    scanf("%d%d%d%d",&n,&X,&Y,&m);    fo(i,1,m)    {        scanf("%d%d",&p[i].x,&p[i].y);        p[i].num=i;    }    p[++m].x=X,p[m].y=Y;    sort(p+1,p+m+1,comp);    p[++m].x=n,p[m].y=0;    fo(i,1,m) to[p[i].num]=i;    scanf("%d",&q);    for(i=q;i>=1;i--)    {        scanf("%d",&opt);        if(opt==1)        {            scanf("%d",&ask[i]);            ask[i]=to[ask[i]];            flag[ask[i]]=1;        }    }     //构造凸包    s[0]=0;top=1;    fo(i,1,n) if(!flag[i]) {s[1]=i;break;}    now=dis(p[s[0]],p[s[1]]);    fo(i,s[1]+1,m) if(!flag[i])    {        while(top && cross(p[s[top-1]],p[s[top]],p[i])>0)        {            now-=dis(p[s[top-1]],p[s[top]]);            top--;        }        now+=dis(p[s[top]],p[i]);        s[++top]=i;    }    fo(i,0,top) tree.insert(p[s[i]]);    fo(i,1,q)     {        if(!ask[i]) //输出答案         {            ans[++tot]=now;            continue;        }        set <node> ::iterator tmp,q,sta;        sta=tmp=tree.lower_bound(p[ask[i]]);        if((tmp->x)>p[ask[i]].x) tmp--,sta--;        tx=tmp->x,ty=tmp->y;tmp++;wx=tmp->x,wy=tmp->y;tmp--;        if( cross((node){tx,ty,0},p[ask[i]],(node){wx,wy,0}) > 0) continue;  //如果点被包含        now-=dis((node){tx,ty,0},(node){wx,wy,0});  //把新点左右两点的距离减掉         while(1)   //左点向左进行删除操作         {            q=tmp;            tx=tmp->x,ty=tmp->y;tmp--;            if(tx==0 && ty==0)            {                now+=dis((node){0,0,0},p[ask[i]]);                break;            }            ex=tmp->x,ey=tmp->y;tmp++;            node h1={tx,ty,0},h2={ex,ey,0};            if(cross(p[ask[i]],h1,h2)<0)            {                tmp--,now-=dis(h1,h2);                tree.erase(q);            }            else {now+=dis(h1,p[ask[i]]);break;}        }        tmp=tree.lower_bound(p[ask[i]]);        if((tmp->x)<=p[ask[i]].x) tmp++;        while(1) //右点向右进行删除操作         {            q=tmp;            tx=tmp->x,ty=tmp->y;tmp++;            if(tx==n && ty==0)            {                now+=dis((node){n,0,0},p[ask[i]]);                break;            }            ex=tmp->x,ey=tmp->y;tmp--;            node h1={tx,ty,0},h2={ex,ey,0};            if(cross(p[ask[i]],h1,h2)>0)            {                tmp++,now-=dis(h1,h2);                tree.erase(q);            }            else {now+=dis(h1,p[ask[i]]);break;}        }        tree.insert(p[ask[i]]);    }    for(i=tot;i>=1;i--) printf("%.2lf\n",ans[i]);    return 0;}
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 被手机店骗话费送合约机怎么办 机蜜租赁的手机丢了怎么办 合约机返话费手机掉了怎么办 手机摔坏了开不了机怎么办 华为魅特10屏幕锁住了怎么办 电信送的路由器只有一个端口怎么办 苹果手机5c屏幕死机了怎么办 日版苹果手机显示无服务怎么办 iphone有锁4g变3g怎么办 耳机胶套脱落掉入耳朵怎么办 苹果笔记本电用完了开机了怎么办 华为荣耀5x锁屏密码忘了怎么办 华为荣耀锁屏密码忘了怎么办 华为手机屏锁密码忘了怎么办 华为荣耀9青春版相机很模糊怎么办 华为荣耀7手机照相忽然模糊怎么办 苹果5s进水开不了机怎么办 红米4a玩王者荣耀卡怎么办 老是显示媒体存储以停止运行怎么办 支付宝邮二维码地址留错了怎么办 华为畅享5s太卡怎么办 金立手机导航gps信号弱怎么办 贴了膜后边缘有气泡怎么办 魅族mx5刷机失败然后黑屏怎么办 魅族手机无线网玩王者卡怎么办 魅族手机玩王者荣耀卡怎么办 魅族手机导航gps信号弱怎么办 魅族手机4g信号差怎么办 魅族手机下面一排键失灵怎么办 魅族手机冲不进去电怎么办 苹果账号付款了又让付款怎么办 魅蓝3s开机定屏怎么办 魅族手机未找到固件怎么办不用电脑 魅族手机触屏局部失灵怎么办 更新了魅蓝的新系统掉帧怎么办 手机后盖摔了一下凹了一个洞怎么办 魅族手机充电需要重启怎么办 网上预约好了但就诊卡丢了怎么办 如果魅族手机锁屏密码忘记了怎么办 魅族手机应用加密忘记密码了怎么办 小米电视盒子3s死机黑屏怎么办