結果

問題 No.3344 Common Tangent Line
コンテスト
ユーザー ponjuice
提出日時 2025-11-14 11:26:00
言語 C++23
(gcc 13.3.0 + boost 1.87.0)
結果
AC  
実行時間 961 ms / 3,000 ms
コード長 10,527 bytes
コンパイル時間 3,646 ms
コンパイル使用メモリ 299,536 KB
実行使用メモリ 7,716 KB
最終ジャッジ日時 2025-11-14 11:26:42
合計ジャッジ時間 36,999 ms
ジャッジサーバーID
(参考情報)
judge4 / judge2
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
sample AC * 1
other AC * 40
権限があれば一括ダウンロードができます

ソースコード

diff #

#include<bits/stdc++.h>
using namespace std;

using ll = long long;
#define rep(i,n) for(ll i = 0; i < n; i++)
#define all(a) a.begin(),a.end()

using Real = long double;
using Point = complex<Real>;
const Real EPS = 1e-8, PI = acos(-1);

istream &operator>>(istream &is, Point& p) {
  Real a, b;
  is >> a >> b;
  p = Point(a, b);
  return is;
}
ostream &operator<<(ostream &os, Point p) {
  return os << fixed << p.real() << " " << p.imag();
}

Point operator*(Point p, Real d) {
  return Point(real(p) * d, imag(p) * d);
}

Point operator/(Point p, Real d) {
  return Point(real(p) / d, imag(p) / d);
}

inline bool equal(Real a, Real b){
    return fabs(a - b) < EPS;
}

Point unit(Point a) {
    return a / abs(a);
}

Point normal(Point a) {
    return a * Point(0, 1);
}

Point normalUnit(Point a) {
    return unit(normal(a));
}

Real dot(Point a, Point b){
    return a.real() * b.real() + a.imag() * b.imag(); 
}

Real cross(Point a, Point b){
    return (a.real() * b.imag() - a.imag() * b.real());
}

Point rotate(Point a, double theta){
    return Point(cos(theta) * a.real() - sin(theta) * a.imag(),
                 sin(theta) * a.real() + cos(theta) * a.imag());
}

Real radianToDegree(Real r) {
    return r * 180 / PI;
}

Real degreeToRadian(Real d) {
    return d * PI / 180;
}

Real getAngle(Point a, Point b, Point c){
    const Point ab(b - a), bc(c - b);
    Real s = atan2(ab.imag(), ab.real()), t = atan2(bc.imag(), bc.real());
    if(s > t) swap(s,t);
    Real theta = t - s;
    return min(theta, 2 * PI - theta);
}

struct Line{
    Point a,b;

    Line() = default;

    Line(Point A, Point B) : a(A), b(B) {}

    // ax + by = c
    Line(Real A, Real B, Real C){
        if(equal(A, 0)) a = Point(0, C / B), b = Point(1, C / B);
        else if(equal(B, 0)) b = Point(C / A, 0), b = Point(C / A, 1);
        else a = Point(0, C / B), b = Point(C / A, 0);
    }

    friend ostream &operator<<(ostream &os, Line &p) {
        return os << p.a << " to " << p.b;
    }

    friend istream &operator>>(istream &is, Line &a) {
        return is >> a.a >> a.b;
    }
};

using Segment = Line;

// 点pから線lに垂線を下ろした時の交点
Point projection(Line l, Point p){
    Real t = dot(p - l.a, l.b - l.a) / norm(l.b - l.a);
    return l.a + (l.b - l.a) * t;
}

int ccw(Point a, Point b, Point c){
    b -= a;
    c -= a;
    // 反時計回り
    if(cross(b, c) > EPS) return 1;
    // 時計回り
    if(cross(b, c) < -EPS) return -1;
    // c-a-bの順番で点がある
    if(dot(b, c) < 0) return -2;
    // a-b-cの順番で点がある
    if(norm(b) < norm(c)) return 2;
    // a-b の間にcがある
    return 0;
}

bool isParallel(Line a, Line b){
    return equal(cross(a.b - a.a, b.b - b.a), 0);
}

bool isOrthogonal(Line a, Line b){
    return equal(dot(a.b - a.a, b.b - b.a), 0);
}

bool isIntersect(Segment s, Segment t){
    return ccw(s.a, s.b, t.a) * ccw(s.a, s.b, t.b) <= 0 && ccw(t.a, t.b, s.a) * ccw(t.a, t.b, s.b) <= 0;
}

Point crossPoint(Line s, Line t){
    Real d1 = cross(s.b - s.a, t.b - t.a);
    Real d2 = cross(s.b - s.a, s.b - t.a);
    if(equal(d1, 0) && equal(d2, 0)){
        if( ccw(t.a, t.b, s.a) == 0) return s.a;
        if( ccw(t.a, t.b, s.b) == 0) return s.b;
        if( ccw(s.a, s.b, t.a) == 0) return t.a;
        if( ccw(s.a, s.b, t.b) == 0) return t.b;
    }

    return t.a + (t.b - t.a) * (d2 / d1);
}

Real distance(Segment l, Point p){
    if(dot(l.b - l.a, p - l.a) < EPS) return abs(p - l.a);
    if(dot(l.a - l.b, p - l.b) < EPS) return abs(p - l.b);
    return abs(cross(l.b - l.a, p - l.a)) / abs(l.b - l.a);
}

Real ldistance(Line l, Point p){
    return abs(cross(l.b - l.a, p - l.a)) / abs(l.b - l.a);
}

Real distance(Segment s, Segment t){
    if(isIntersect(s,t)) return 0;
    Real res = distance(s,t.a);
    res = min(res, distance(s, t.b));
    res = min(res, distance(t, s.a));
    res = min(res, distance(t, s.b));
    return res;
}

Real area(vector<Point>& p){
    Real res = 0;
    int n = p.size();
    rep(i,n){
        res += cross(p[i], p[(i + 1) % n]);
    }

    return res / 2;
}

bool isConvex(vector<Point>& p){
    bool res = true;
    int n = p.size();
    rep(i,n){
        if(cross(p[(i+1)%n]-p[i], p[(i+2)%n] - p[(i+1)%n]) < -EPS){
            res = false;
        }
    }
    return res;
}

int isContain(vector<Point>& g, Point& p){
    bool in = false;
    int n = g.size();
    rep(i,n){
        Point a = g[i] - p, b = g[(i+1)%n] - p;
        if(imag(a) > imag(b)){
            swap(a, b);
        }
        if(imag(a) <= EPS && EPS < imag(b) && cross(a, b) < - EPS){
            in = !in;
        }
        if( equal(cross(a, b), 0) && dot(a, b) <= 0){
            return 1;
        }
    }
    return in ? 2 : 0;
}

// 直線を含めない場合は <=-EPS を <EPS に変更する
vector<Point> convexHull(vector<Point>& p){
   int n = (int)p.size(), k = 0;
    sort(all(p), [](const Point &a, const Point &b) {
        return (real(a) != real(b) ? real(a) < real(b) : imag(a) < imag(b));
    });
    vector<Point> ch(2 * n);
    for(int i = 0; i < n; ch[k++] = p[i++]) {
        while(k >= 2 && cross(ch[k - 1] - ch[k - 2], p[i] - ch[k - 1]) <= -EPS)
            --k;
    }
    for(int i = n - 2, t = k + 1; i >= 0; ch[k++] = p[i--]) {
        while(k >= t && cross(ch[k - 1] - ch[k - 2], p[i] - ch[k - 1]) <= -EPS)
            --k;
    }
    ch.resize(k - 1);
    return ch;
}

Real convexDiameter(vector<Point>& p){
    int n = (int)p.size();
    int is = 0, js = 0;

    for(int i = 1; i < n; i++){
        if(imag(p[i]) > imag(p[is])) is = i;
        if(imag(p[i]) < imag(p[js])) js = i;
    }
    Real mx = abs(p[js] - p[is]);
    int i = is, j = js;

    do {
        if(cross(p[(i + 1) % n] - p[i], p[(j + 1) % n] - p[j]) >= 0){
            j = (j + 1) % n;
        }else{
            i = (i + 1) % n;
        }
        if(abs(p[i] - p[j]) > mx){
            mx = abs(p[i] - p[j]);
        }
    }while(i != is || j != js);

    return mx;
}

vector<Point> convexCut(vector<Point>& p, Line l){
    vector<Point> res;
    int n = (int)p.size();

    for(int i = 0; i < n; i++){
        if(ccw(l.a, l.b, p[i]) != -1) res.emplace_back(p[i]);
        if(ccw(l.a, l.b, p[i]) * ccw(l.a, l.b, p[(i + 1) % n]) < 0){
            res.emplace_back(crossPoint(Line(p[i], p[(i + 1) % n]), l));
        }
    }

    return res;
}

struct Circle{
    Point p;
    Real r;

    Circle() = default;
    Circle(Point p, Real r) : p(p), r(r) {}
};

int isIntersect(Circle a, Circle b){
    double d = abs(a.p - b.p);
    // 2つの円が離れている場合
    if(d > a.r + b.r + EPS) {
        return 4;
    }
    // 外接している場合
    if(equal(d, a.r + b.r)) {
        return 3;
    }
    // 内接している場合
    if(equal(d, abs(a.r - b.r))) {
        return 1;
    }
    // 内包している場合
    if(d < abs(a.r - b.r) - EPS) {
        return 0;
    }
    return 2;
}

Circle inCircle(Point a, Point b, Point c){
    Real A = abs(b - c), B = abs(a - c), C = abs(a - b);
    Point p(A * real(a) + B * real(b) + C * real(c),
                A * imag(a) + B * imag(b) + C * imag(c));
    p /= (A + B + C);
    Real r = distance(Line(a, b), p);
    return Circle(p, r);
}

Circle outCircle(Point a, Point b, Point c){
    Point f = (a + b) / 2, s = (b + c) / 2;
    Line A(f, normal(b - f) + f), B(s, normal(c - s) + s);
    Point m = crossPoint(A,B);
    Real r = abs(a - m);
    r = max(r, abs(b - m));
    r = max(r, abs(c - m));
    return Circle(m, r);
}

vector<Point> crossPoint(Circle c, Line l) {
    vector<Point> res;
    Real d = ldistance(l, c.p);
    // 交点を持たない
    if(d > c.r + EPS) {
        return res;
    }
    // 接する
    Point h = projection(l, c.p);
    if(equal(d, c.r)) {
        res.emplace_back(h);
        return res;
    }
    Point e = unit(l.b - l.a);
    Real ph = sqrt(c.r * c.r - d * d);
    res.emplace_back(h - e * ph);
    res.emplace_back(h + e * ph);
    return res;
}

vector<Point> crossPoint(Circle c1, Circle c2) {
    vector<Point> res;
    int mode = isIntersect(c1, c2);
    Real d = abs(c1.p - c2.p);
    if(mode == 4) {
        return res;
    }
    if(mode == 0) {
        return res;
    }
    if(mode == 3) {
        Real t = c1.r / (c1.r + c2.r);
        res.emplace_back(c1.p + (c2.p - c1.p) * t);
        return res;
    }
    if(mode == 1) {
        if(c2.r < c1.r - EPS) {
            res.emplace_back(c1.p + (c2.p - c1.p) * (c1.r / d));
        } else {
            res.emplace_back(c2.p + (c1.p - c2.p) * (c2.r / d));
        }
        return res;
    }
    Real rc1 = (c1.r * c1.r + d * d - c2.r * c2.r) / (2 * d);
    Real rs1 = sqrt(c1.r * c1.r - rc1 * rc1);
    if(c1.r - abs(rc1) < EPS) {
        rs1 = 0;
    }
    Point e12 = (c2.p - c1.p) / abs(c2.p - c1.p);
    res.emplace_back(c1.p + rc1 * e12 + rs1 * e12 * Point(0, 1));
    res.emplace_back(c1.p + rc1 * e12 + rs1 * e12 * Point(0, -1));
    return res;
}

vector<Point> tangentToCircle(Point p, Circle c) {
    return crossPoint(c, Circle(p, sqrt(norm(c.p - p) - c.r * c.r)));
}

vector<Line> tangent(Circle a, Circle b) {
    vector<Line> ret;
    // 2円の中心間の距離
    Real g = abs(a.p - b.p);
    // 円が内包されている場合
    if(equal(g, 0)) {
        return ret;
    }
    Point u = unit(b.p - a.p);
    Point v = rotate(u, PI / 2);
    for(int s : {-1, 1}) {
        double h = (a.r + b.r * s) / g;
        if(equal(h * h, 1)) {
            ret.emplace_back(a.p + (h > 0 ? u : -u) * a.r,
                             a.p + (h > 0 ? u : -u) * a.r + v);

        } else if(1 - h * h > 0) {
            Point U = u * h, V = v * sqrt(1 - h * h);
            ret.emplace_back(a.p + (U + V) * a.r, b.p - (U + V) * (b.r * s));
            ret.emplace_back(a.p + (U - V) * a.r, b.p - (U - V) * (b.r * s));
        }
    }
    return ret;
}

void solve() {
    Point p1, p2;
    Real r1, r2;
    cin >> p1 >> r1 >> p2 >> r2;
    Circle c1(p1, r1), c2(p2, r2);

    vector<Line> ret = tangent(c1, c2);
    Real ans = 0;

    rep(i,ret.size()) {
        Real a, b, c;
        // ax + by + c = 0 という形にする
        a = ret[i].b.imag() - ret[i].a.imag();
        b = ret[i].a.real() - ret[i].b.real();
        c = - (a * ret[i].a.real() + b * ret[i].a.imag());

        Real mx = max({abs(a), abs(b), abs(c)});
        a /= mx; b /= mx; c /= mx;

        ans += abs(a + b + c);
    }

    cout << ans << endl;
}

int main() {
    cout << fixed << setprecision(20);
    int t;
    cin >> t;
    while(t--) solve();
}
0