【BOI2012】Mobile___思维+单调队列

326 篇文章 0 订阅
206 篇文章 0 订阅

题目大意:

著名的手机网络运营商Totalphone 修建了N个基站收发台,以用于把信号网络覆盖一条新建的高速公路,因为Totalphone 的程序员总是很马虎的,所以,基站的传功功率不能独立设置,只能将所有新基站的功率设置为一个相同的值。为了让能源的消耗尽量少,给出N个基站的坐标[xi,yi]公司希望知道公路中任意点到最近基站距离的最大值。

高速公路是一条从(0,0) 到(L,0) 的直线线段
-10^9<=xi,yi<=10^9
1<=N<=10^6
1<=L<=10^9

题解:

假设所有n个点x坐标均不相同。如果有两个点x坐标相同,那么显然只需要保留y坐标绝对值较小的一个即可。
对于相邻顺序的两个点,作两点连线段的中垂线。显然,中垂线左侧的点距离x坐标较小的点较近,中垂线右侧的点距离x坐标较大的点较近。
按x坐标的顺序从小到大扫描n个整点,用一个栈保存可能成为最近点的整点,同时维护栈中两点连线段中垂线与给出线段交点x坐标单调递增,用单调队列实现。
因为对于2点间中垂线与公路的交点j而言,如果他不是单调递增的,否则离它最近的距离必定不会是j到这2点的距离。
借用某dalao博客的一幅图:
这里写图片描述
D为BC中垂线与公路交点,此时D到B和到C的距离最远,不过D的最近距离必然不可能是BD因为他在E的左侧,而应该是AD,所以要维护这些焦点递增。
记住还要判断两侧端点
时间复杂度:O(N)

代码:

pascal莫名超时,翻C++后AC。
P:

var
    x,y:array [0..1000001] of real;
    cmax:array [0..1000001] of longint;
    head,tail,i,n,m,l,x1,y1:longint;
    k,j,ans,rp1,rp2:real;

function num(x1,y1,x2,y2:real):real;
begin
     exit((Sqr(x1)+sqr(y1)-sqr(x2)-sqr(y2))/(2*(x1-x2)));
end;

begin
    assign(input,'mobile.in'); reset(input);
    assign(output,'mobile.out'); rewrite(output);
       readln(m,l);
       n:=1;
       readln(x[1],y[1]);
       for i:=2 to m do
         begin
               readln(x1,y1);
               if x[n]=x1 then
                  begin
                     if abs(y1)<abs(y[n]) then y[n]:=y1
                  end
               else begin
                         inc(n);
                         x[n]:=x1;
                         y[n]:=y1;
                    end;
         end;

       ans:=maxlongint;
       for i:=1 to n do
            if sqrt(sqr(x[i])+sqr(y[i]))<ans then ans:=sqrt(sqr(x[i])+sqr(y[i]));
       k:=maxlongint;
       for i:=1 to n do
           if sqrt(sqr(x[i]-l)+sqr(y[i]))<k then k:=sqrt(sqr(x[i]-l)+sqr(y[i]));
       if k>ans then ans:=k;
       head:=1;
       tail:=1;
       cmax[1]:=1;
       for i:=2 to n do
         begin
               while (head<tail) do
                 begin
                     rp1:=num(x[cmax[head]],y[cmax[head]],x[cmax[head+1]],y[cmax[head+1]]);
                     if rp1>=0 then break;
                     inc(head);
                 end;
               while (head<tail) do
                 begin
                     rp1:=num(x[cmax[tail]],y[cmax[tail]],x[cmax[tail-1]],y[cmax[tail-1]]);
                     rp2:=num(x[i],y[i],x[cmax[tail]],y[cmax[tail]]);
                     if rp2>=rp1 then break;
                     dec(tail);
                 end;
               inc(tail);
               cmax[tail]:=i;
         end;
       for i:=head to tail-1 do
          begin
                j:=num(x[cmax[i]],y[cmax[i]],x[cmax[i+1]],y[cmax[i+1]]);
                if j>l then break;
                k:=sqrt(sqr(x[cmax[i]]-j)+sqr(y[cmax[i]]));
                if k>ans then ans:=k;
          end;
       writeln(ans:0:4);
      close(input); close(output);
end.

C:

#include <bits/stdc++.h>

#define sqr(x) (x) * (x)

using namespace std;

int m,cmax[1000001];
double l,x[1000001],y[1000001];


long double num(double x1,double y1,double x2,double y2)
{
    return((sqr(x1)+sqr(y1)-sqr(x2)-sqr(y2))/(2*(x1-x2)));
}

int main()
{
    freopen("mobile.in","r",stdin);
    freopen("mobile.out","w",stdout);
    scanf("%d%lf",&m,&l);
    int n=1;
    scanf("%lf%lf",&x[1],&y[1]);
    for (int i=2; i<=m; i++)
    {
        double x1,y1;
        scanf("%lf%lf",&x1,&y1);
        if (x1!=x[n])
        {
             n++;
             x[n]=x1;
             y[n]=y1;
        }
        else 
        if (x1==x[n]&&abs(y1)<abs(y[n]))
            y[n] =y1;
    }   
    long double ans=9999999999999;
    for (int i=1; i<=n; i++)
        if (sqrt(sqr(x[i])+sqr(y[i]))<ans)
            ans=sqrt(sqr(x[i])+sqr(y[i]));

    long double k=9999999999999;
    for (int i=1; i<=n; i++)
        if (sqrt(sqr(x[i]-l)+sqr(y[i]))<k)
            k=sqrt(sqr(x[i]-l)+sqr(y[i]));
    ans=max(ans,k);

    cmax[1]=1;
    int tail=1,head=1;
    for (int i=2; i<=n; i++)
    {
        while ((head<tail)&&num(x[cmax[head]],y[cmax[head]],x[cmax[head+1]],y[cmax[head+1]])<0)
            head++;
        while ((head<tail)&&num(x[cmax[tail]],y[cmax[tail]],x[cmax[tail-1]],y[cmax[tail-1]])>num(x[i],y[i],x[cmax[tail]],y[cmax[tail]]))
            tail--;
        cmax[++tail]=i;
    }

    for (int i=head; i<tail; i++)
    {
        k=num(x[cmax[i]],y[cmax[i]],x[cmax[i+1]],y[cmax[i+1]]);
        if (k>l) break;
        long double j=sqrt(sqr(x[cmax[i]]-k)+sqr(y[cmax[i]]));
        ans=max(ans,j);
    }
    printf("%.5lf\n",(double)ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值