cf的地址
因为校强, "咕咕十段"队获得了EC-final的参赛资格
因为我弱, "咕咕十段"队现在银面很大
于是咕咕十段决定进行训练. 周末vp了一场, 这是赛后补题.
vp的时候想到了可以利用边界和切线, 但是没有仔细思考.
后来发现, 找到可以走的边界和切线之后, bfs一下就完事了啊.
题目给的eps限制使得我们可以合法的沿着切线和边界进行行走
关键在于找出"可以走的边界和切线"
如果一条切线在两个切点之间穿过了一个圆, 那么就不走这条切线(肯定可以走中间那个圆的某几条切线)
一条切线可以向两个切点外侧延伸, 直到与某个圆/边界相交.
主要麻烦的地方在于找圆的切线以及内外公切线. 以及我现在竟然能把直线求交写错...

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const double eps = 1e-7;
struct point{
    double x, y;
    point(double a = 0.0, double b = 0.0):x(a), y(b){}
    point operator - (const point &A)const{
        return point(x-A.x, y-A.y);
    }
    point operator + (const point &A)const{
        return point(x+A.x, y+A.y);
    }
    void read(){
        scanf("%lf%lf",&x,&y);
    }
    point rot(double theta)const{
        return point(x*cos(theta)-y*sin(theta), x*sin(theta) + y*cos(theta));
    }
    double length()const{
        return sqrt(x*x+y*y);
    }
    point norm()const {
        return point(x/length(), y/length());
    }
    void output(){
        printf("%lf %lf",x,y);
    }
}pL, pR, S, T, C[52];
double r[52];
double cross(const point &A, const point &B){
    return A.x * B.y -A.y * B.x;
}
struct Line{
    point S, dlt;
    double tmax, tmin;
    Line(){
        tmax = 1e20;
        tmin = -1e20;
    }
    Line(point s, point t){
        S = s;
        dlt = t - s;
        tmax = 1e20;
        tmin = -1e20;
    }
    Line(point s, point t, double t1, double t2){
        S = s; dlt = t - s;
        tmin = t1; tmax = t2;
    }
    point operator () (const double &t)const{
        return S + point(dlt.x*t, dlt.y*t);
    }
    bool through(const point &p)const {
        return fabs(cross(p-S, dlt))<eps;
    }
    double gett(const point &p)const{
        if(fabs(p.x - S.x) > eps){
            return (p.x - S.x)/(dlt.x);
        }else if(fabs(p.y - S.y)>eps){
            return (p.y - S.y)/(dlt.y);
        }else{
            return 0;
        }
    }
    bool contain(const point &p)const{
        if(fabs(cross(p-S, dlt))>eps)return false;
        double t = gett(p);
        return t>=tmin-eps&&t<=tmax+eps;
    }
    point getp(double t)const{
        return point(S.x+t*dlt.x, S.y+t*dlt.y);
    }
    void output(){
        S.output();(S+dlt).output();
    }
}L[6000];
Line tangent(point P, point C, double r, int flag){
    double theta = acos(r/(P-C).length());
    point v = (P-C).norm();
    v = point(v.x*r, v.y*r);
    v = v.rot(flag*theta);
    point T = C + v;
    return Line(P, T);
}
Line tangent_out(point C1, double r1, point C2, double r2, int flag){
    if((fabs(r1-r2))<eps){
        point v = (C1-C2).norm();
        v = point(v.x * r1, v.y * r1);
        v = v.rot(flag*acos(-1)/2);
        return Line(C1+v, C2+v);
    }else{
        if(r1>r2){
            swap(C1, C2);
            swap(r1, r2);
        }
        //r1<r2
        //r1/r2 = (l1)/(l2), l2-l1 = length(C1-C2)
        double L = (C1 - C2).length();
        L = L * r2/(r2-r1);
        point v = (C1-C2).norm();
        double theta = flag * acos(r2/L);
        v = v.rot(theta);
        return Line(C1 + point(v.x*r1, v.y*r1), C2 + point(v.x*r2, v.y*r2));
    }
}
Line tangent_in(point C1, double r1, point C2, double r2, int flag){
    point v = (C2 - C1);
    point P = C1 + point(v.x*r1/(r1+r2), v.y*r1/(r1+r2));
    double theta = flag*acos(r1/(P-C1).length());
    v = v.norm().rot(theta);
    return Line(C1 + point(v.x*r1, v.y*r1), C2 - point(v.x*r2,v.y*r2));
}
int cnt_lines = 0, cnt_lines2 = 0;
point intersect(const Line &L1, const Line &L2){
    double C = cross(L1.dlt, L2.dlt);
    if(fabs(C)<eps)return point(1e20, 1e20);
    else{
        double t = cross(L1.S - L2.S, L2.dlt)/cross(L2.dlt, L1.dlt);
        return L1(t);
    }
}
double mult(const point &P1, const point &P2){
    return P1.x*P2.x + P1.y*P2.y;
}
bool between(point P, Line &L){
    point S = L.S, T = L.S + L.dlt;
    return mult(S-P, T-P) < -eps;
}
bool intersect(Line &L, point C, double r){
    double dis = fabs(cross(C-L.S, L.dlt))/L.dlt.length();
    if(dis > r - eps)return false;
    double theta = acos(dis/r);
    int flag = cross(C-L.S, L.dlt) > eps ? (1) : (-1);
    point v = L.dlt.norm().rot(flag * acos(-1)/2 + theta);
    point p = C + point(v.x*r, v.y*r);
    if(between(p, L)){
        return true;
    }else{
        double t = L.gett(p);
        if(t < 0 && t > L.tmin){
            L.tmin = t;
        }
        if(t > 0 && t < L.tmax){
            L.tmax = t;
        }
        v = L.dlt.norm().rot(flag * acos(-1)/2 - theta);
        p = C + point(v.x*r, v.y*r);
        t = L.gett(p);
        if(t < 0 && t > L.tmin){
            L.tmin = t;
        }
        if(t > 0 && t < L.tmax){
            L.tmax = t;
        }
        return false;
    }
}
int N;
bool visited[6000];
int pre[6000], dis[6000];
int q[6000];
void output(int x, int cnt){
    if(pre[x] == 0){
        printf("%d\n",cnt);
    }else{
        output(pre[x], cnt+1);
        point p = intersect(L[x], L[pre[x]]);
        p.output();printf("\n");
    }
}
void bfs(){
    int head = 0, tail = 0;
    visited[0] = true; dis[0] = 0;//S
    for(int i = 1; i <= cnt_lines2; ++i){
        if(L[i].contain(S)){
            pre[i] = 0; dis[i] = 1;
            visited[i] = true;
            q[tail++] = i;
        }
    }
    while(head!=tail){
        int x = q[head++];
        if(L[x].contain(T)){
            output(x, 0);
            break;
        }else{
            for(int i=1;i<=cnt_lines2;++i){
                if(!visited[i]){
                    point p = intersect(L[i],L[x]);
                    if(L[i].contain(p)&&L[x].contain(p)){
                        q[tail++] = i;
                        visited[i] = true;
                        pre[i] = x;
                        dis[i] = dis[x] + 1;
                    }
                }
            }
        }
    }
}
int main(){
    scanf("%d",&N);
    pL.read();pR.read();
    S.read();T.read();
    for(int i = 1;i <= N;++i){
        C[i].read();scanf("%lf", r+i);
    }
    L[++cnt_lines] = Line(S, T);
    for(int i = 1;i <= N;++i){
        L[++cnt_lines] = tangent(S, C[i], r[i], 1);
        L[++cnt_lines] = tangent(S, C[i], r[i], -1);
        L[++cnt_lines] = tangent(T, C[i], r[i], 1);
        L[++cnt_lines] = tangent(T, C[i], r[i], -1);
    }
    for(int i = 1;i <= N;++i){
        for(int j = i + 1;j <= N;++j){
            L[++cnt_lines] = tangent_out(C[i], r[i], C[j], r[j], 1);
            L[++cnt_lines] = tangent_out(C[i], r[i], C[j], r[j],-1);
            L[++cnt_lines] = tangent_in(C[i], r[i], C[j], r[j], 1);
            L[++cnt_lines] = tangent_in(C[i], r[i], C[j], r[j],-1);
        }
    }
    for(int i=1;i<=cnt_lines;++i){
        bool flag = true;
        for(int j = 1;j <= N;++j){
            if(intersect(L[i], C[j], r[j])){
                flag = false;break;
            }
        }
        if(flag){
            L[++cnt_lines2] = L[i];
        }
    }
    L[++cnt_lines2] = Line(point(pL.x, pL.y), point(pL.x, pR.y), 0, 1);
    L[++cnt_lines2] = Line(point(pR.x, pL.y), point(pR.x, pR.y), 0, 1);
    L[++cnt_lines2] = Line(point(pL.x, pL.y), point(pR.x, pL.y), 0, 1);
    L[++cnt_lines2] = Line(point(pL.x, pR.y), point(pR.x, pR.y), 0, 1);
    for(int i=1;i<=cnt_lines2 - 4;++i){
        for(int j=cnt_lines2 - 3;j<=cnt_lines2;++j){
            point p = intersect(L[i], L[j]);
            double t = L[i].gett(p);
            if(t<0&&t>L[i].tmin){
                L[i].tmin = t;
            }
            if(t>0&&t<L[i].tmax){
                L[i].tmax = t;
            }
        }
    }
    bfs();
    return 0;
}
01-05 15:07