题目
题目链接:https://atcoder.jp/contests/abc181/tasks/abc181_f
有一个 (200 imes 2 imes 10^9) 的网格,其中横坐标区间为 ([-100,100]),纵坐标区间为 ([-10^9,10^9])。平面上有若干个点。
你有一个圆,最开始在 ((0,-10^9)),询问圆半径最大是多少使得它可以在不接触点的情况下圆心到达 ((0,10^9))。
思路
二分圆的直径,如果两个点之间的距离小于直径,那么显然圆没法从这两个点之间经过。注意要把直线 (x=100) 和 (x=-100) 也看做一个点。
那么如果连完线之后把直线 (x=100) 和 (x=-100) 联通,那么相当于存在若干连线将左右两边隔开,这样圆就无法过去。否则可以到达。
用并查集维护即可。
时间复杂度 (O(n^2log k))。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=110;
const double eps=1e-5;
int n,S,T,X[N],Y[N],father[N];
int getdis(int i,int j)
{
return (X[i]-X[j])*(X[i]-X[j])+(Y[i]-Y[j])*(Y[i]-Y[j]);
}
int find(int x)
{
return x==father[x]?x:father[x]=find(father[x]);
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d%d",&X[i],&Y[i]);
S=n+1; T=n+2;
double l=0,r=200,mid;
while (r-l>=eps)
{
mid=(l+r)/2.0;
for (int i=1;i<=T;i++) father[i]=i;
for (int i=1;i<=n;i++)
for (int j=i+1;j<=n;j++)
if (getdis(i,j)<mid*mid) father[find(i)]=find(j);
for (int i=1;i<=n;i++)
{
if (100+Y[i]<mid) father[find(i)]=find(S);
if (100-Y[i]<mid) father[find(i)]=find(T);
}
if (find(S)!=find(T)) l=mid;
else r=mid;
}
printf("%0.10lf
",l/2.0);
return 0;
}