参考:
- https://oi-wiki.org/geometry/inverse/
- https://blog.csdn.net/acdreamers/article/details/16966369
- https://jingyan.baidu.com/article/77b8dc7f8a792e6174eab623.html
知识点:圆的反演
反演中心 O,半径R,若 P 与 P' 满足:
- 点 (P') 在射线(overrightarrow {OP})上
- (|OP|cdot |OP'| = R^2)
则 P' 是 P 关于 圆 O 的反演
性质:
- 圆外反演到圆内,反之亦然,圆O上的点反演为其自身
- 不过点O的圆A,其反演的圆也不过点O
- 过O的圆A,其反演图像是过点O的直线
- 两个圆相切,则他们的反演图像也相切
反演公式:
记圆O半径(R),圆心坐标((x_0,y_0))
圆A半径(r_1),圆心坐标((x_1, y_1))
则圆A关于圆O的反演:圆B的半径为:
[r_2={1over 2}R^2({1over |OA|-r_1}-{1over |OA|+r_1})
]
另外圆心B与O的距离(|OB|) 有:
[r_2={1over 2}R^2({1over |OA|-r_1}+{1over |OA|+r_1})
]
圆心B坐标:
[x_2 = x_0 + {|OB|over|OA|} (x1 - x_0) \ y_2 = y_0 + {|OB|over|OA|} (y1 - y_0)
]
//圆 c 关于 p 反演所得到的圆
circle inverse(circle c, Point p, db R){
db OA = c.p.distance(p);
db x = OA - c.r, y = OA + c.r;
circle res;
res.r = 0.5 * R * R * (1.0/x - 1.0/y);
db OB = 0.5 * R * R * (1.0/x + 1.0/y);
res.p = p + (c.p - p) * (OB/OA);
return res;
}
// AB直线关于圆P反演
circle inverse_l2c(Point p, Point A, Point B, db R){
circle res;
Point q = lineprog(p, A, B); //p在AB上面的映射
db dis = q.distance(p);
res.r = R * R * 0.5 / dis;
res.p = p + (q - p) / dis * res.r;
return res;
}
回到这一题:
根据性质3可以知道,反演不改变相切的关系,所以关于圆P(半径随意定),将两个圆反演,求出这两个圆的切线,然后再将切线反演后,得到与原本的两个圆相外切的两个圆
将两个圆反演之后,这两个圆只需要求外公切线即可,并且这个外公切线,还需要满足一些条件才能被选做答案
首先为什么要求外公切线:
设点A在圆B上
圆B关于圆A反演得到直线CD,现在圆E是内切于圆B中,关于圆A反演得到圆F,可以发现圆心F,与点A是在CD异侧的,其实这个现象不难解释,从圆反演的定义即可推导出。
那么相反的,如果一个圆外切于圆B,那么关于圆A的反演得到的圆的圆心,一定与A在CD的同侧。
这样就解释了为什么题目中的两个圆反演之后需要求外公切线,另外,这条外公切线还必须使得点A和那两个反演圆的圆心在同侧
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
#define dbg(x...) do { cout << "