题目大意
给定平面内n个圆
定义两个圆Ci,Cj的距离为|OiOj|−ri−rj。求最近圆对的距离。
一个测试点T组数据。
题目分析
考虑二分这个距离d,怎么检验这个距离呢?
将每个圆半径扩展
现在问题变成怎么快速判断是否存在相交的圆。
考虑将所有圆分成左右两条竖直扫描线。我们按照x坐标顺序一一枚举这些扫描线。假设我们当前枚举到的最后一条左扫描线是
那么现在算法流程就很清晰了。
考虑枚举所有扫描线,如果是左扫描线,就加入圆并且检查与其圆心
无论是加入还是删除都要检查相交情况,因为删除圆会使原先在加入时(圆心
这个数据结构使用set就好了。
总时间复杂度O(nlogXlogn)。
代码实现
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cctype>
#include <cmath>
#include <set>
using namespace std;
typedef long double db;
int read()
{
int x=0,f=1;
char ch=getchar();
while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
return x*f;
}
const db EPS=1e-9;
const int N=50005;
bool equ(db x,db y){return fabs(x-y)<=EPS;}
int sgn(db x){return equ(x,0)?0:(x<0?-1:1);}
db sqr(db x){return x*x;}
struct P
{
db x,y;
P (db x_=0,db y_=0){x=x_,y=y_;}
};
P operator+(P p,P q){return P(p.x+q.x,p.y+q.y);}
P operator-(P p,P q){return P(p.x-q.x,p.y-q.y);}
P operator*(P p,db k){return P(p.x*k,p.y*k);}
db operator*(P p,P q){return p.x*q.x+p.y*q.y;}
db operator^(P p,P q){return p.x*q.y-p.y*q.x;}
db mod2(P p){return p*p;}
db mod(P p){return sqrt(mod2(p));}
db dist(P p,P q){return mod(q-p);}
struct C
{
P O;
db r;
C (){}
C (P O_,db r_=0){O=O_,r=r_;}
}cir[N],c[N];
bool is_intersectant(C c1,C c2){return sgn(c1.r+c2.r-dist(c1.O,c2.O))>=0;}
int l[2][N],RANK[N],KTH[N];
int T,n;
db X[2],Y[2];
struct cid
{
int id;
cid (int id_=0){id=id_;}
bool operator<(const cid &y)const{return RANK[id]<RANK[y.id];}
};
bool cmp(int x,int y){return sgn(c[x].O.y-c[y].O.y)<0;}
set<cid> t;
db left(int x){return cir[x].O.x-cir[x].r;}
db right(int x){return cir[x].O.x+cir[x].r;}
bool cmp1(int x,int y){return sgn(left(x)-left(y))<0;}
bool cmp2(int x,int y){return sgn(right(x)-right(y))<0;}
bool erase_circle(int x)
{
set<cid>::iterator it=t.find(cid(x));
if (it!=t.begin())
{
--it;
if (is_intersectant(cir[(*it).id],cir[x])) return 1;
++it;
}
if ((++it)!=t.end()&&is_intersectant(cir[(*it).id],cir[x])) return 1;
return t.erase(--it),0;
}
bool insert_circle(int x)
{
set<cid>::iterator it=t.insert(cid(x)).first;
if (it!=t.begin())
{
--it;
if (is_intersectant(cir[(*it).id],cir[x])) return 1;
++it;
}
if ((++it)!=t.end()&&is_intersectant(cir[(*it).id],cir[x])) return 1;
return 0;
}
bool judge(db d)
{
t.clear();
for (int i=1;i<=n;++i) cir[i]=C(c[i].O,c[i].r+d),l[0][i]=l[1][i]=i;
sort(l[0]+1,l[0]+1+n,cmp1),sort(l[1]+1,l[1]+1+n,cmp2);
t.insert(l[0][1]);
for (int lcur=2,rcur=1;lcur<=n||rcur<=n;)
if (lcur==n+1||sgn(left(l[0][lcur])-right(l[1][rcur]))>0)
{
if (erase_circle(l[1][rcur++])) return 1;
}
else if (insert_circle(l[0][lcur++])) return 1;
return 0;
}
db binary_search()
{
db ret=0.;
for (db l=0.,r=dist(P(X[0],Y[0]),P(X[1],Y[1])),mid;sgn(r-l)>=0;)
{
mid=(l+r)/2.;
if (judge(mid)) r=mid-EPS;
else l=(ret=mid)+EPS;
}
return ret*2.;
}
int main()
{
freopen("moonmist.in","r",stdin),freopen("moonmist.out","w",stdout);
for (T=read();T--;)
{
n=read();
for (int i=1,x,y,r;i<=n;++i) x=read(),y=read(),r=read(),c[i]=C(P(x,y),r),KTH[i]=i;
sort(KTH+1,KTH+1+n,cmp),X[0]=X[1]=c[1].O.x,Y[0]=Y[1]=c[1].O.y;
for (int i=1;i<=n;++i) RANK[KTH[i]]=i,X[0]=min(X[0],c[i].O.x),Y[0]=min(Y[0],c[i].O.y),X[1]=max(X[1],c[i].O.x),Y[1]=max(Y[1],c[i].O.y);
printf("%.6lf\n",(double)binary_search());
}
fclose(stdin),fclose(stdout);
return 0;
}