【计几】反演变换题集

这两篇博客探讨了反演变换在解决几何问题中的重要作用。第一篇介绍了HDU-6097题目的解决方案,通过反演变换找到圆内两点经过圆上某点的最短距离。第二篇则涉及ICPC2013杭州赛区的Problem of Apollonius问题,讨论如何求过两圆外一点且与两圆外切的所有圆,详细阐述了反演变换的注意事项和应用技巧。
摘要由CSDN通过智能技术生成


HDU-6097 Mindis【圆内两点经过圆上某点的最短距离】

题解:多校6 HDU-6097 Mindis 几何数学

有一个结论是:两个以一对互反演点为其中一个端点,以反演中心和反演圆上某个点为另两个点为端点的三角形,相似,且相似比与反演点与反演半径的比例有关。

AC核心代码:

int main()
{
    int t; scanf("%d", &t);
    while(t--)
    {
        point a, b;
        double r, ans, len;
        scanf("%lf %lf %lf %lf %lf",&r, &a.x, &a.y, &b.x, &b.y);

        if(length(a) < eps) ans = 2 * r; //函数ptp(point a, point p, double r)输入要保证a和p不重合
        else
        {
            len = length(a);
            a = ptp(a, {0,0}, r);
            b = ptp(b, {0,0}, r);

            double dis = dis_to_line({0, 0}, a, b);
            if(dcmp(dis, r) <= 0) ans = get_dis(a, b);
            else
            {
                point e = (a + b) / 2;
                e = norm(e) * r;
                ans = get_dis(e, a) + get_dis(e, b);
            }
            ans = len * ans / r;
        }
        
        printf("%.8f\n",ans);
    }

    // system("pause");
    return 0;
}

「ICPC 2013 杭州赛区」Problem of Apollonius

题意:求过两圆外一点,且与两圆外切的所有的圆。

题解:- 反演变换 - OI Wiki

有几个坑要注意:

  1. 公切线有可能过反演中心,此时要舍去。
  2. 直线反演后内外侧会反转。
    内公切线反演后,与两圆一个内切一个外切,要舍去。
    对于外公切线,当反演点与两圆中心在同侧时,反演后新圆与两圆外切,保留。
    当反演点与两圆中心在异侧时,反演后新圆与两圆内切,舍去。

AC代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;

const int N=1e5+10;
typedef long long LL;
const double eps = 1e-7;
const double pi = acos(-1);
int dcmp(double x, double y){
    if(fabs(x-y) < eps) return 0;
    if(x < y) return -1;
    return 1;
}
int sign(double x){
    if(fabs(x) < eps) return 0;
    if(x < 0) return -1;
    return 1;
}

struct point{
    double x,y;
    point (double x=0, double y=0):x(x),y(y){}
    bool operator < (point a){
        if(dcmp(x, a.x)) return dcmp(x, a.x) < 0;
        return dcmp(y, a.y) < 0;
    }
    bool operator == (point a) { return !dcmp(x, a.x) && !dcmp(y, a.y); }
};
point operator + (point a, point b) { return {a.x + b.x, a.y + b.y}; }
point operator - (point a, point b) { return {a.x - b.x, a.y - b.y}; }
point operator * (point a, double b) { return {a.x * b, a.y * b}; }
point operator / (point a, double b) { return {a.x / b, a.y / b}; }

double dot(point a, point b) { return a.x * b.x + a.y * b.y; }
double cross(point a, point b) { return a.x * b.y - a.y * b.x; }
double area(point a, point b, point c) { return cross(b - a, c - a) / 2; }
double length(point a) { return sqrt(a.x * a.x + a.y * a.y); }
double project(point a, point b, point c) { return dot(c - a, b - a) / length(b - a); }
point rotate(point a, double rad) { return {a.x * cos(rad) - a.y * sin(rad), a.x * sin(rad) + a.y * cos(rad)}; }
point norm(point a) { return a / length(a); }
double get_dis(point a, point b) { return length(a - b); }


double dis_to_line(point p, point a,point b)
{
    if(a == b) return get_dis(p, a);
    point v1 = b - a, v2 = p - a;
    return fabs(cross(v1, v2) / length(v1));
}

//圆

struct circle{
    point o;
    double r;
    point norm(double rad) { return {o.x + r * cos(rad),o.y + r * sin(rad)}; } // 返回圆上圆心角为rad的点
};
circle ctc(circle c, point p, double r)
{
    circle res;
    double t = get_dis(p, c.o);
    double x = r * r / (t - c.r), y = r * r / (t + c.r);
    res.r = (x - y) / 2;

    double s = (x + y) / 2;
    res.o = p + norm(c.o - p) * s;
    return res;
}
circle ltc(point a, point b, point p, double r)
{
    double d = dis_to_line(p, a, b);
    d = r * r / d;
    circle res;
    res.r = d / 2;

    point v;
    if(area(a, b, p) > 0) v = rotate(b - a, -pi/2);
    else v = rotate(a - b, -pi/2);
    res.o = p + norm(v) * res.r;
    return res;
}

int get_tangents(circle A, circle B, point* a, point* b)
{
    int cnt = 0;
    if(dcmp(A.r, B.r) < 0) {swap(A, B);swap(a, b);}
    double d2 = (A.o.x - B.o.x)*(A.o.x - B.o.x) + (A.o.y - B.o.y)*(A.o.y - B.o.y); //圆心距离
    double base = atan2(B.o.y - A.o.y, B.o.x - A.o.x); // AB向量的极角,A位大圆
    double ang = acos((A.r - B.r) / sqrt(d2));//外公切线的图
    // cout<<"### "<<d2<<" "<<base<<" "<<ang<<endl;
    a[cnt] = A.norm(base + ang); b[cnt] = B.norm(base + ang); cnt++;
    a[cnt] = A.norm(base - ang); b[cnt] = B.norm(base - ang); cnt++;
    return cnt;
}

circle A, B;
point C;
point ta[10], tb[10];
point ansa[10], ansb[10];
int pos;

int main()
{
    int t; scanf("%d", &t);
    while(t--)
    {
        pos = 0;
        scanf("%lf %lf %lf %lf %lf %lf %lf %lf", &A.o.x, &A.o.y, &A.r, &B.o.x, &B.o.y, &B.r, &C.x, &C.y);
        A = ctc(A, C, 10); B = ctc(B, C, 10);

        // cout<<"## "<<A.o.x<<" "<<A.o.y<<" "<<A.r<<endl<<B.o.x<<" "<<B.o.y<<" "<<B.r<<endl;

        int cnt = get_tangents(A, B, ta, tb);

        for(int i=0; i<cnt; i++)
            if(sign(area(ta[i], tb[i], C)) && sign(area(ta[i], tb[i], C)) * sign(area(ta[i], tb[i], A.o)) >= 0) ansa[pos] = ta[i], ansb[pos++] = tb[i];
        // for(int i=0; i<cnt; i++) cout<<"# "<<ta[i].x<<" "<<ta[i].y<<endl<<tb[i].x<<" "<<tb[i].y<<endl;

        printf("%d\n", pos);
        for(int i=0; i<pos; i++)
        {
            circle ans = ltc(ansa[i], ansb[i], C, 10);
            // cout<<"# "<<ansa[i].x <<" "<<ansa[i].y<<" "<<ansb[i].x<<" "<<ansb[i].y<<endl;
            if(fabs(ans.o.x) < eps) ans.o.x = 0;
            if(fabs(ans.o.y) < eps) ans.o.y = 0;
            printf("%.6f %.6f %.6f\n", ans.o.x, ans.o.y, ans.r);
        }
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值