BalticOI2012 移动网络

· · 题解

2022.11.8 Update

#### 题意 > $~~~~$ 有一条从 $(0,0)$ 到 $(0,l)$ 的线段与 $n$ 个点,找到线段上的一个点使其离平面给定的所有点最小距离最大。求这个最大距离。 > $~~~~$ $1\leq n \leq 10^6

题意

$~~~~$ 对于某个二分的距离,则当线段能被 所有平面上的点以该距离作圆覆盖时 该距离是合法的。由圆方程可以解出一个圆心为 $(x,y)$ ,半径为 $r$ 的圆与 $x$ 轴的交点横坐标为 $\pm\sqrt{r^2-y^2}+x$ 。 $~~~~$ 按照正常思路我们就需要对区间进行排序然后 $O(n)$ 判定是否能覆盖整条线段,但这样复杂度就达到了 $\mathcal{O(n \log \dfrac{\sqrt{5}l}{\epsilon}\log n)}$ ,再考虑时限比较紧,显然需要优化。 $~~~~$ 细想发现我们还有给定的坐标横坐标、纵坐标依次递增的性质没用到。若依次考虑,当 $x$ 相同时,显然 $|y|$ 最小的那个点最可能更新覆盖区间的右端点。然后考虑横坐标不同的三个点 $p_1,p_2,p_3$ ,如果 $p_1$ 与 $p_2$ 画出的圆没有相交,但与 $p_3$ 画出的圆相交,则显然 $p_3$ 的右端点也会比 $p_2$ 的右端点更靠右,也就是 $p_3$ 更新的区间会比 $p_2$ 更优,因此不用排序也是正确的。因此我们就去掉了 $\log n$ 的复杂度。 #### 代码 ```cpp #include <cmath> #include <cstdio> #include <algorithm> using namespace std; int n,L; int X[1000005],Y[1000005]; bool check(double r) { double x=0,From,To; for(int i=1;i<=n;i++) { From=-sqrt(1.0*r*r-1.0*Y[i]*Y[i])+X[i]; To=sqrt(1.0*r*r-1.0*Y[i]*Y[i])+X[i]; if(From<=x&&x<=To) x=To; } return x>=L; } int main() { scanf("%d %d",&n,&L); for(int i=1;i<=n;i++) scanf("%d %d",&X[i],&Y[i]); double l=0,r=2236068000,mid,Ans; while(r-l>0.0005) { mid=(l+r)/2; if(check(mid)) r=mid,Ans=mid; else l=mid; } printf("%.6f",Ans); return 0; } ```