这题是RujiaLiu的几何专场中,第四简单的题目了。
果断无脑流,果断需要敲无数行模板,果断需要考虑各种情况。
还好srgba给的sample照顾到了所有情况,并且没有太卡精度,不过没有spj,不得不说老uva在这一点上太二了。
题目六部分,六个和圆有关的问题。以及附上我的模板:
预备知识(模板):
eps
sgn符号函数
点(向量)类:
构造、加、减、数乘(放缩)、点积、叉积、比较器、反向、法向、单位向量、向量以任意角度旋转。
直线类:
构造、旋转、平移、判点其上、算点距离、点投影、点对称(后加的,无关)、求交点。
圆类:
两点构造、三点构造(外接圆)、点半径构造、点在内or上or外、求点到圆的切线。
六个问题:
三角形外接圆:
根据垂径定理搞定,两线求交(不是自己写的),精度比较高的方法是叉积算比例求定比分点什么的。
三角形内切圆:
抄的别人的模板。(我觉得这个最难,虽然我自己也写了一个吧,后来删掉了,因为丢精度比较厉害。就是用那种算夹角,旋转向量再求交的方法)
给定一点一圆,求过该点的圆切线:
两种情况(第三种是圆内点,无解):一,点在圆上,直接求出点到圆心的矢量的法向量,就是所求;二,点在圆外,求出点心矢量后,根据切线三角形,算出旋转角,向量对称旋转即得。
给定一点一线和半径求圆:
四种情况:一,圆直径小于点线距离,无解;二,等于,唯一解,求出点在直线投影,两点连线中点即为圆心;三,圆直径大于点线距离,做个梯形,算一下夹角,对称旋转即可出两解;四,点在线上,点平移即可。
两条线加半径定圆:
因为线不平行,所以一定有解,且是四个解:即把每条线向两个方向平移半径长度即可确定2*2个圆。
两个圆加半径确定圆:
三种情况:两圆距离(圆心距离减半径和)大于新圆直径,无解:等于,唯一解,圆外线段中点,不好求,那么就是两圆连线上(ra+r)/(ra+rb+2*r)出的分点;小于,两解,构造三角形,算出定比分点比例,垂直平移分点即可。
上海比赛的时候模板确实用上了- -不过还是没出题。太悲剧了。自己还是太弱了啊- -。
附上我的2D几何模板吧~略长~387lines
/*
   point class && line class && circle class 
   Many things about circle and so on
   WTF
 */
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
#define maxn 1005
#define sqr(x) ((x) * (x))
int n;
const double eps = 1e-8;
const double pi = acos(-1.0);
int sgn(double x)
{
    return fabs(x) < eps ? 0 : x < -eps ? -1: 1;
}
struct point
{
    double x,y;
    point(){}
    point(double a,double b):x(a),y(b){}
    point operator - (point p){return point(x - p.x,y - p.y);}
    point operator + (point p){return point(x + p.x,y + p.y);}
    double norm(){return sqrt(sqr(x) + sqr(y));}
    double norm2(){return sqr(x) + sqr(y);}
    double operator * (point p){return x * p.x + y * p.y;}
    point operator * (double a){return point(x * a,y * a);}
    double operator ^ (point p){return x * p.y - y * p.x;}
    bool operator == (point p){return sgn(x-p.x)==0&&sgn(y-p.y)==0;}
    bool operator < (const point &p){return sgn(x - p.x) < 0 || (sgn(x - p.x) == 0 && sgn(y - p.y) < 0);}
    point rev(){return point(-x,-y);}
    point ver(){return point(y,-x);}
    point unit(){return point(x / norm(),y / norm());}
    point rotate(double theta)//the rotate function for vector to rotate
    {//in counter-clockwise direction
        return point(x*cos(theta)-y*sin(theta),x*sin(theta)+y*cos(theta));
    }
}p[maxn];
struct line
{
    point p,k;
    line(){}
    //MIND THIS!!!!!
    line(point a,point b):p(a),k(b){}//a is the fixed point,k is the directed vector
    line rotate(double theta){line l;l.p=p;l.k=k.rotate(theta);return l;}
    bool on(point q){return sgn((q-p) ^ k) == 0;}
    //move the line acording to the parpendicular-direction with offset distance
    line move(double offset)//to which direction??
    {//every time we use this,we can try the bi-direction
        point vec = k.ver();
        vec = vec.unit() * offset;
        point q = p + vec;
        return line(q,k);
    }
    //distance from a point to a line
    double dis(point q)
    {
        point a = p,b = a + k;
        return fabs((a - q) ^ (b - q)) / k.norm();
    }
    //cast the point to the line and find the pedal
    /*
       by construct the line through the 'q' parallel to the origin line
       calc the lq.p 's vector to 'q'
       add it onto the origin line's p
     */
    point cast(point q)//something wrong
    {
        double d = line(p,k).dis(q);
        line lq = line(p,k).move(d);
        point vec;
        if(!lq.on(q))
            lq = line(p,k).move(-d);
        vec = q - lq.p;
        return p + vec;
    }
    //calc the symmetrical point of the given one againt the given line
    point sym(point q)
    {
        point c = line(p,k).cast(q);
        point vec = c - q;
        return c + vec;
    }
    //copied from some big cow
    //by vector product and "division point"
    int cross(line q,point &ans)
    {
        point a = p,b = p + k,c = q.p,d = q.p + q.k;
        ans = d;
        double rate = ((b-a)^(a-d)) / ((b-a)^(c-d));
        ans = ans + ((c - d) * rate);
    }
};
struct circle
{
    point o;
    double r;
    circle(){}
    circle(point _o,double _r):o(_o),r(_r){}
    //by two points to calc the minimum circle
    circle(point a,point b)
    {
        o = (a + b) * 0.5;
        r = (a - o).norm();
    }
    //give three points (not on the same line)to calc the decided circle
    circle(point a,point b,point c)
    {
        point m1 = (a + b) * 0.5,m2 = (b + c) * 0.5;
        line l1 = line(m1,(b-a).ver()),l2 = line(m2,(c-b).ver());
        l1.cross(l2,o);
        r = (a-o).norm();
    }
    //judge a point in or on the circle or not
    bool in(point x)
    {
        return sgn((x - o).norm() - r) <= 0;
    }
    //judge a point on the circular of the circle or not
    bool inandon(point x)
    {
        return sgn((x - o).norm() - r) == 0;
    }
    //give a point and a cirlce,calc the two tangent line (just angles)
    //by rotation ::mode 1
    //if the point p is on the circular of the circle
    //just output one tangentline ::mode 2
    void tangentline(point p,double &th1,double &th2,int mode)
    {
        double alpha = atan2(o.y - p.y,o.x - p.x);
        if(mode == 1)
        {
            th1 = alpha + pi * 0.5;
            if(sgn(th1 - pi) >= 0)
                th1 -= pi;
            else if(sgn(th1) < 0)
                th1 += pi;
            th1 = th1 / pi * 180.0;
        }
        else
        {
            //circle center and point p 's connected line 's angle
            double theta = asin(r / (p - o).norm());
            th1 = alpha + theta;
            while(sgn(th1 - pi) >= 0)
                th1 -= pi;
            while(sgn(th1) < 0)
                th1 += pi;
            th2 = alpha - theta;
            while(sgn(th2 - pi) >= 0)
                th2 -= pi;
            while(sgn(th2) < 0)
                th2 += pi;
            th1 = th1 / pi * 180.0;
            th2 = th2 / pi * 180.0;
        }
    }
};
//copied from some big cow
//to calculate the insribed circle of a triangle
circle inscribed(point a,point b,point c)
{
    point a1 = a + (b-a).unit() + (c - a).unit(),b1 = b + (a-b).unit()+(c-b).unit();
    point ans;
    line(a,a1-a).cross(line(b,b1-b),ans);
    double r = line(a,b-a).dis(ans);
    return circle(ans,r);
}
int onelineonepoint(point p,line l,double r,circle &a,circle &b)
{
    double d = l.dis(p);
    if(sgn(d - 2.0 * r) > 0)
        return 0;
    else if(sgn(d - 2.0 * r) == 0)
    {
        point c = l.cast(p);
        a = circle((c + p) * 0.5,r);
        return 1;
    }
    else if(sgn(d - 2.0 * r) < 0 && sgn(d) > 0)
    {
        double det,theta;
        det = d - r;
        theta = acos(det / r);
        point c = l.cast(p);
        point to = c - p;
        to = to.unit() * r;
        point vec1 = to.rotate(theta),vec2 = to.rotate(-theta);
        a = circle(p + vec1,r);
        b = circle(p + vec2,r);
        return 2;
    }
    else if(sgn(d) == 0)//the point is on the line
    {
        point per = l.k.ver();
        per = per.unit() * r;
        a = circle(p + per,r);
        b = circle(p + per.rev(),r);
        return 2;
    }
}
//use two cross lines and one radius to calc four circles
//circle - answer array
circle four[10];
//use the center of circle to compare circles
bool cmpcircle(circle a,circle b)
{
    return a.o < b.o;
}
void twolineoneradius(line a,line b,double r)
{
    line A[2],B[2];
    A[0] = a.move(r),A[1] = a.move(-r);
    B[0] = b.move(r),B[1] = b.move(-r);
    int cnt = 1;
    for(int i = 0;i < 2;i++)
        for(int j = 0;j < 2;j++)
        {
            point temp;
            A[i].cross(B[j],temp);
            four[cnt++] = circle(temp,r);
        }
}
//use two disjoint circle to calc one or two new circles tangent to them with given 'r'
//not in the middle line !!!!
int twocircle(circle a,circle b,double r,circle &c,circle &d)
{
    point dir = (b.o - a.o);
    double dis = dir.norm();
    double len = dis - a.r - b.r;
    double ra = r + a.r,rb = r + b.r;
    if(sgn(len - 2.0 * r) > 0)
        return 0;
    else if(sgn(len - 2.0 * r) == 0)
    {
        point to = dir * (ra / (ra + rb));
        c = circle(a.o + to,r);
        return 1;
    }
    else
    {
        //r1^2 - l1^2 = r2^2 - l2^2
        //l1 + l2 = l
        //wtf
        double tempto = (sqr(ra) - sqr(rb) + sqr(dis)) / (2.0 * dis);
        point to = dir * (tempto / dis);
        point temp = (a.o + to);
        point per = to.ver();
        double rate = sqrt(sqr(ra) - to.norm2())/ per.norm();
        per = per * rate;
        point per2 = per.rev();
        c = circle(temp + per,r);
        d = circle(temp + per2,r);
        return 2;
    }
}
//we judge three points on the same line or not
bool oneline(point a,point b,point c){return sgn((a - b) ^ (a - c)) == 0;}
char opt[1000];
char type[][1000] = {"CircumscribedCircle","InscribedCircle","TangentLineThroughPoint","CircleThroughAPointAndTangentToALineWithRadius","CircleTangentToTwoLinesWithRadius","CircleTangentToTwoDisjointCirclesWithRadius"};
int main()
{
    while(scanf("%s",opt) == 1)
    {
        int mark = -1;
        for(int i = 0;i < 6;i++)
        {
            if(strcmp(opt,type[i]) == 0)
            {
                mark = i;
                break;
            }
        }
        if(mark == 0)
        {
            for(int i = 0;i < 3;i++)
            {
                double x,y;
                scanf("%lf %lf",&x,&y);
                p[i] = point(x,y);
            }
            circle ans = circle(p[0],p[1],p[2]);
            printf("(%.6lf,%.6lf,%.6lf)\n",ans.o.x+1e-10,ans.o.y+1e-10,ans.r+1e-10);
        }
        else if(mark == 1)
        {
            for(int i = 0;i < 3;i++)
            {
                double x,y;
                scanf("%lf %lf",&x,&y);
                p[i] = point(x,y);
            }
            circle ans = inscribed(p[0],p[1],p[2]);
            printf("(%.6lf,%.6lf,%.6lf)\n",ans.o.x+1e-10,ans.o.y+1e-10,ans.r+1e-10);
        }
        else if(mark == 2)
        {
            double x,y,r;
            scanf("%lf %lf %lf",&x,&y,&r);
            circle o = circle(point(x,y),r);
            scanf("%lf %lf",&x,&y);
            double t1,t2;
            point p = point(x,y);
            if(o.in(p))
            {
                if(o.inandon(p))
                {
                    o.tangentline(p,t1,t2,1);
                    printf("[%.6lf]\n",t1+1e-10);
                }
                else
                    puts("[]");
            }
            else
            {
                o.tangentline(p,t1,t2,2);
                if(sgn(t1 - t2) > 0)
                    swap(t1,t2);
                printf("[%.6lf,%.6lf]\n",t1+1e-10,t2+1e-10);
            }
        }
        else if(mark == 3)
        {
            double x,y;
            for(int i = 0;i < 3;i++)
            {
                scanf("%lf %lf",&x,&y);
                p[i] = point(x,y);
            }
            double r;
            scanf("%lf",&r);
            circle a,b;
            int mod = onelineonepoint(p[0],line(p[1],p[2]-p[1]),r,a,b);
            if(mod == 0)
                puts("[]");
            else if(mod == 1)
                printf("[(%.6lf,%.6lf)]\n",a.o.x+1e-10,a.o.y+1e-10);
            else
            {
                if(b.o < a.o)
                    swap(a,b);
                printf("[(%.6lf,%.6lf),(%.6lf,%.6lf)]\n",a.o.x+1e-10,a.o.y+1e-10,b.o.x+1e-10,b.o.y+1e-10);
            }
        }
        else if(mark == 4)
        {
            double x,y,r;
            for(int i = 0;i < 4;i++)
            {
                scanf("%lf %lf",&x,&y);
                p[i] = point(x,y);
            }
            scanf("%lf",&r);
            twolineoneradius(line(p[0],p[1]-p[0]),line(p[2],p[3]-p[2]),r);
            sort(four + 1,four + 5,cmpcircle);
            putchar('[');
            for(int i = 1;i <= 4;i++)
                printf("(%.6lf,%.6lf)%c",four[i].o.x+1e-10,four[i].o.y+1e-10,i == 4?']':',');
            puts("");
        }
        else if(mark == 5)
        {
            double x,y,r;
            scanf("%lf %lf %lf",&x,&y,&r);
            circle a(point(x,y),r);
            scanf("%lf %lf %lf",&x,&y,&r);
            circle b(point(x,y),r);
            scanf("%lf",&r);
            circle c,d;
            int mod = twocircle(a,b,r,c,d);
            if(mod == 0)
                puts("[]");
            else if(mod == 1)
                printf("[(%.6lf,%.6lf)]\n",c.o.x+1e-10,c.o.y+1e-10);
            else
            {
                if(d.o < c.o)
                    swap(c,d);
                printf("[(%.6lf,%.6lf),(%.6lf,%.6lf)]\n",c.o.x+1e-10,c.o.y+1e-10,d.o.x+1e-10,d.o.y+1e-10);
            }
        }
    }
}
å