http://acm.hdu.edu.cn/showproblem.php?pid=6097
我发现仅仅看着那些圆的反演的性质,然而还是不会做题。。。。
我们把p点对圆c进行反演得到p2,对q进行反演得到q2,
由于三角形ODP~三角形OP2D,因为|OD|*|OD|=r^2=|OP|*|OP2|,所以相似,那么|PD|/|DP2|=|OD|/|OP2|
这样P2D+Q2D最小就等价于PD+QD最小
那么P2Q2如果与圆有交点,那么两点之间线段最短,D就是与圆的交点
否则,则是道P2Q2的中垂线与圆的交点最短,直接算出中点mid与C的圆心(0,0)的角度,然后把D算出来就行了。
#include<bits/stdc++.h>
using namespace std;
const double eps=1e-12;
inline int sgn(double x)
{
if(x>-eps && x<eps) return 0;
if(x>0) return 1;
else return -1;
}
struct point
{
double x,y;
point(double a=0,double b=0)
{
x=a;y=b;
}
point operator + (const point b)const
{
return point(x+b.x,y+b.y);
}
point operator - (const point b)const
{
return point(x-b.x,y-b.y);
}
point operator * (const double t)const
{
return point(t*x,t*y);
}
};
struct circle
{
point o;
double r;
};
inline double det(const point a,const point b)
{
return a.x*b.y-a.y*b.x;
}
inline double sqr(double x) {return x*x;}
inline double dist(const point a,const point b)
{
return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y));
}
inline double mysqrt(double x)
{
x=max(0.0,x);
return sqrt(x);
}
circle c;
point p,q,d;
double ans;
inline void prework()
{
c.o=point(0,0);
scanf("%lf",&c.r);
scanf("%lf%lf%lf%lf",&p.x,&p.y,&q.x,&q.y);
}
inline point inverse_point(point p,circle a)
{
point res;
double len=dist(p,a.o);
double len2=a.r*a.r/len;
res=a.o+(p-a.o)*(len2/len);
return res;
}
inline void circle_cross_line(point a,point b,point o,double r,point ret[],int &num)
{
double x0=o.x,y0=o.y;
double x1=a.x,y1=a.y;
double x2=b.x,y2=b.y;
double dx=x2-x1,dy=y2-y1;
double A=dx*dx+dy*dy;
double B=2*dx*(x1-x0)+2*dy*(y1-y0);
double C=sqr(x1-x0)+sqr(y1-y0)-sqr(r);
double delta=B*B-4*A*C;
num=0;
if(sgn(delta)>=0)
{
double t1=(-B-mysqrt(delta))/(2*A);
double t2=(-B+mysqrt(delta))/(2*A);
if(sgn(t1-1)<=0&&sgn(t1)>=0)
ret[num++]=point(x1+t1*dx,y1+t1*dy);
if(sgn(t2-1)<=0&&sgn(t2)>=0)
ret[num++]=point(x1+t2*dx,y1+t2*dy);
}
}
inline void mainwork()
{
if(sgn(p.x-q.x)==0 && sgn(p.y-q.y)==0)
{
ans=(c.r-dist(c.o,p))*2;
return;
}
point p2=inverse_point(p,c);
point q2=inverse_point(q,c);
double d=fabs(det(p2-c.o,q2-c.o))/dist(p2,q2);
if(sgn(d-c.r)<=0)
{
point ret[3];int num=0;
circle_cross_line(p2,q2,c.o,c.r,ret,num);
point d=ret[0];
ans=dist(p,d)+dist(q,d);
}
else
{
point mid=point((p.x+q.x)/2,(p.y+q.y)/2);
double a=atan2(mid.y,mid.x);
point d=point(c.r*cos(a),c.r*sin(a));
ans=dist(p,d)+dist(q,d);
}
}
inline void print()
{
printf("%.8f\n",ans);
}
int main()
{
int t;
scanf("%d",&t);
for(int i=1;i<=t;i++)
{
prework();
mainwork();
print();
}
return 0;
}