【BOI2012】Mobile(mobile)

题目

在一个平面内有n个基站,每个基站有个坐标(xi,yi),现在要给每个基站设定一个相同的值,使基站覆盖x轴上(0,m)的所有点,求这个值最小要是多少。
PS:输入的点已按x为第一关键字y为第二关键字排序。

题解

①过滤无用点。②在此基础上利用单调栈维护对答案有用的点。③计算答案。
所谓无用点,就是当x相同时,abs(y)不是最小的点。(这很显然)
考虑正确答案的规律。

如果面前有一道题目,十分地棘手,那么很可能会有一个结论让你推,那请你根据题目给你的条件猜测一下这个结论是什么。这一点很重要。

假设有这么三个点A,B,C,设(x1,0)为AB的中垂线与X轴的交点,(x2,0)为BC的中垂线与X轴的交点。若 x2<x1 则点B为没用的点。详见图①。
这里写图片描述
x轴下方红色括号括起来的部分是A点比B点优的部分,绿色代表B比A优。
x轴下方绿色括号括起来的部分是B点比C点优的部分,绿色代表C比B优。
由于 YC<YB ,所以黄色部分优于其下方绿色部分。
很显然,上方绿色部分没有其下方的红色部分优。所以淘汰掉B点。
维护个单调栈就可以实现步骤②。
最后统计的答案大致为两点(最后筛出来的)中垂线与X轴的交点与其中一点的距离的最大值。
PS:注意边界。这是个很恶心的地方。
我们可以知道中垂线与X轴的交点的横坐标x单调递增。所以
①刚刚x进入[0,L]时,判断一下

ans=max(ans,min(dis(0,0,x[d[i1]],y[d[i1]]),dis(0,0,x[d[i]],y[d[i]])))

x[0,L]
ans=max(ans,dis(temp,0,x[d[i]],y[d[i]]))

③x刚刚出[0,L]时,判断一下
ans=max(ans,min(dis(L,0,x[d[i1]],y[d[i1]]),dis(L,0,x[d[i]],y[d[i]])))

然而还有一个更坑爹的地方,一定要考虑到。
④x一直 [0,L] 时,
ans=max(ans,dis(0,0,x[d[1]],y[d[1]]))
ans=max(ans,dis(L,0,x[d[tail]],y[d[tail]]))

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 1000010
#define DB double
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
int i,j,k,l,n,m,tot,last;
DB x[N],y[N],L;
DB K,B,X1,Y1,X,Y;
DB temp,temp1,ans,mx;
int head,tail,d[N];
DB point(int i,int j){
    DB K,X1,Y1,B;
    K=(y[i]-y[j])/(x[i]-x[j]);
    if(K==0){
        return (x[j]+x[i])/2;
    }
    K=-1/K;
    X1=(x[i]+x[j])/2;
    Y1=(y[i]+y[j])/2;
    B=Y1-X1*K;
    return -B/K;
}
DB dis(DB X0,DB Y0,DB X1,DB Y1){return sqrt((X0-X1)*(X0-X1)+(Y0-Y1)*(Y0-Y1));}
int main(){
    freopen("mobile.in","r",stdin);
    freopen("mobile.out","w",stdout);
    scanf("%d%lf",&n,&L);
    x[0]=-2147483647;
    fo(i,1,n){
        scanf("%lf%lf",&X,&Y);
        if(X>x[tot]){
            tot++;
            x[tot]=X;
            y[tot]=Y;
        } else
        if(X==x[tot])if(abs(Y)<abs(y[tot]))y[tot]=Y;
    }
    last=0;
    d[1]=0;
    tail=0;
    fo(i,1,tot){
        while (tail>=2 && point(d[tail-1],d[tail])>=point(d[tail],i)) tail--;
        d[++tail]=i;
    }
    ans=-2147483647;
    temp=point(d[2],d[1]);
    if(temp>0)ans=max(ans,dis(0,0,x[d[1]],y[d[1]]));
    fo(i,2,tail){
        temp=point(d[i],d[i-1]);
        if(temp1<0 && temp>=0)
            ans=max(ans,min(dis(0,0,x[d[i-1]],y[d[i-1]]),dis(0,0,x[d[i]],y[d[i]])));
        if(temp>L && temp1<=L){
            ans=max(ans,min(dis(L,0,x[d[i-1]],y[d[i-1]]),dis(L,0,x[d[i]],y[d[i]])));
        }
        if(temp>=0&&temp<=L)
        ans=max(ans,dis(temp,0,x[d[i]],y[d[i]]));
        temp1=temp;
    }
    if(temp<L)ans=max(ans,dis(L,0,x[d[tail]],y[d[tail]]));
    printf("%.6lf",ans);
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值