题目大意:
著名的手机网络运营商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);
}