HDU-4773 Problem of Apollonius (圆的反演)
参考:
- 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的半径为:
\]
另外圆心B与O的距离\(|OB|\) 有:
\]
圆心B坐标:
\]
//圆 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 << "\033[32;1m" << #x <<" -> "; err(x); } while (0)
void err() { cout << "\033[39;0m" << endl; }
template<class T, class... Ts> void err(const T& arg,const Ts&... args) { cout << arg << " "; err(args...); }
typedef double db;
const db eps = 1e-8;
const db pi = acos(-1);
int sgn(db x){
if(fabs(x) < eps)return 0;
if(x > 0) return 1;
else return -1;
}
inline db sqr(db x){return x * x; }
struct Point{
db x, y;
Point(){}
Point(db x, db y):x(x),y(y){}
void input(){
scanf("%lf%lf", &x, &y);
}
Point operator - (const Point &b){
return Point(x - b.x, y - b.y);
}
db operator ^ (const Point b){
return x * b.y - y * b.x;
}
db operator * (const Point b){
return x * b.x + y * b.y;
}
Point operator + (const Point &b){
return Point(x + b.x, y + b.y);
}
db distance(const Point &b){
return hypot(x-b.x, y-b.y);
}
Point operator *(const db &k)const{
return Point(x * k, y * k);
}
Point operator /(const db &k)const{
return Point(x / k, y / k);
}
Point rotleft(){
return Point(-y, x);
}
db len2(){
return x * x + y * y;
}
}p, a[10], b[10];
// p 在 AB 的投影
Point lineprog(Point p, Point A, Point B){
return A + ( ( (B-A) * ((B-A) * (p-A)) ) / (B-A).len2());
}
struct circle{
Point p;
db r;
circle(){}
circle(Point p, db r):p(p),r(r){}
void input(){
p.input();
scanf("%lf", &r);
}
Point point(double a){
return Point(p.x + cos(a) * r, p.y + sin(a) * r);
}
}c1, c2;
/*
getTangents 函数求出了所有的公切线,由于题目保证了两个圆没有公共点,那么这两个点一定会有四条公切线,我们只需要前两条即外公切线。
*/
// a[i] 存放第 i 条公切线与 圆A 的交点
int getTangents(circle A, circle B, Point*a, Point *b){
int cnt = 0;
// 以A为半径更大的那个圆进行计算
if(A.r < B.r) return getTangents(B, A, b, a);
db d2 = (A.p-B.p).len2(); // 圆心距平方
db rdiff = A.r - B.r; // 半径差
db rsum = A.r + B.r; //半径和
if(d2 < rdiff * rdiff) return 0; // 情况1,内含,没有公切线
Point AB = B.p - A.p; // 向量AB,其模对应圆心距
db base = atan2(AB.y, AB.x); // 求出向量AB对应的极角
if(d2 == 0 && A.r == B.r) return -1;// 情况3,两个圆重合,无限多切线
if(d2 == rdiff * rdiff){ // 情况2,内切,有一条公切线
a[cnt] = A.point(base);
b[cnt] = B.point(base);cnt++;
return 1;
}
// 求外公切线
db ang = acos((A.r - B.r) / sqrt(d2)); //求阿尔法
// 两条外公切线, 此题所需要的公切线
a[cnt] = A.point(base+ang); b[cnt] = B.point(base+ang); cnt++;
a[cnt] = A.point(base-ang); b[cnt] = B.point(base-ang); cnt++;
if(d2 == rsum * rsum){ // 情况5,外切,if里面求出内公切线
a[cnt] = A.point(base); b[cnt] = B.point(pi+base); cnt++;
}
else if(d2 > rsum * rsum){ //情况6,相离,再求出内公切线
db ang = acos((A.r + B.r) / sqrt(d2));
a[cnt] = A.point(base + ang); b[cnt] = B.point(pi+base+ang);cnt++;
a[cnt] = A.point(base - ang); b[cnt] = B.point(pi+base-ang);cnt++;
}
// 此时,d2 < rsum * rsum 代表情况 4 只有两条外公切线
return cnt;
}
//圆 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;
}
// 由直线反演得到圆
circle inverse_l2c(Point p, Point A, Point B, db R){
circle res;
Point q = lineprog(p, A, B);
db dis = q.distance(p);
res.r = R * R * 0.5 / dis;
res.p = p + (q - p) / dis * res.r;
return res;
}
// 判断点CD 是否在 AB 同侧
bool sameside(Point A, Point B, Point C, Point D){
return sgn((C-A) ^ (B-A)) == sgn((D-A) ^ (B-A));
}
int main(){
int T;scanf("%d", &T);
while(T--){
c1.input();
c2.input();
p.input();
c1 = inverse(c1, p, 1.0);
c2 = inverse(c2, p, 1.0);
int cnt = getTangents(c1, c2, a, b);
vector<circle> res;
for(int i=0;i<2;i++){
if(sameside(a[i], b[i], c1.p, c2.p) && sameside(a[i], b[i], c1.p, p)){
circle t = inverse_l2c(p, a[i], b[i], 1.0);
res.push_back(t);
}
}
printf("%d\n",(int)res.size());
for(int i=0;i<res.size();i++){
circle t = res[i];
printf("%.8f %.8f %.8f\n", t.p.x, t.p.y, t.r);
}
}
return 0;
}
HDU-4773 Problem of Apollonius (圆的反演)的更多相关文章
- hdu 4773 Problem of Apollonius
莫名其妙就AC了-- 圆的反演-- 神马是反演? 快去恶补奥数-- #include<iostream> #include<map> #include<string> ...
- 【HDU】4773 Problem of Apollonius
题意 给定相离的两个圆(圆心坐标以及半径)以及圆外的一个定点\(P\),求出过点\(P\)的且与已知的两个圆外切的所有圆(输出总数+圆心.半径). 分析 如果强行解方程,反正我是不会. 本题用到新姿势 ...
- 2017多校第6场 HDU 6097 Mindis 计算几何,圆的反演
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6097 题意:有一个圆心在原点的圆,给定圆的半径,给定P.Q两点坐标(PO=QO,P.Q不在圆外),取圆 ...
- 【 HDU4773 】Problem of Apollonius (圆的反演)
BUPT2017 wintertraining(15) #5G HDU - 4773 - 2013 Asia Hangzhou Regional Contest problem D 题意 给定两个相离 ...
- Pick定理、欧拉公式和圆的反演
Pick定理.欧拉公式和圆的反演 Tags:高级算法 Pick定理 内容 定点都是整点的多边形,内部整点数为\(innod\),边界整点数\(ednod\),\(S=innod+\frac{ednod ...
- 「HDU6158」 The Designer(圆的反演)
题目链接多校8-1009 HDU - 6158 The Designer 题意 T(<=1200)组,如图在半径R1.R2相内切的圆的差集位置依次绘制1,2,3,到n号圆,求面积之和(n< ...
- The Designer (笛卡尔定理+韦达定理 || 圆的反演)
Nowadays, little haha got a problem from his teacher.His teacher wants to design a big logo for the ...
- HOJ 13102 Super Shuttle (圆的反演变换)
HOJ 13102 Super Shuttle 链接:http://49.123.82.55/online/?action=problem&type=show&id=13102 题意: ...
- 圆的反演变换(HDU4773)
题意:给出两个相离的圆O1,O2和圆外一点P,求构造这样的圆:同时与两个圆相外切,且经过点P,输出圆的圆心和半径 分析:画图很容易看出这样的圆要么存在一个,要么存在两个:此题直接解方程是不容易的,先看 ...
随机推荐
- netty心跳检测机制
既然是网络通信那么心跳检测肯定是离不开的,netty心跳检测分为读.写.全局 bootstrap.childHandler(new ChannelInitializer<SocketChanne ...
- springboot源码解析-管中窥豹系列之Initializer(四)
一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
- Maven+Spring 框架,ModelAndView在页面取值不成功
如果创建的是maven project , maven生成的web.xml是这样的: 但是这样是不对的,应该修改成: 下面是代码: <?xml version="1.0" e ...
- SpringBoot嵌入式Servlet容器
SpringBoot默认是将Tomcat作为嵌入式的servlet容器. 问题: 如何修改嵌入式的servlet容器? 1)在配置文件中设置对应的属性值 server.port=8081 # Tomc ...
- 基于腾讯云存储网关 CSG 实现视频在线转码分发
一.背景 随着越来越多的传统业务云化和云端业务发展,数据上云和云端数据处理领域的需求爆发式增长.腾讯云存储网关CSG提供一键部署开箱即用的便捷模式,深度结合COS对象存储生态,为用户提供方便快捷的数据 ...
- 聊聊 g0
很多时候,当我们跟着源码去理解某种事物时,基本上可以认为是以时间顺序展开,这是编年体的逻辑.还有另一种逻辑,纪传体,它以人物为中心编排史事,使得读者更聚焦于某个人物.以一种新的视角,把所有的事情串连起 ...
- for update语句锁机制问题
数据库小知识学习系列 问题: MySQL InnoDB中,select where xxx=123 for update:该xxx没有索引,是使用表锁还是全部数据加行锁? 答: InnoDB引擎(默认 ...
- zabbix 监控tomcat
zabbix 监控tomcat server端rpm -ivh jdk-8u20-linux-x64.rpmvi /etc/profileJAVA_HOME=/usr/java/jdk1.8.0_20 ...
- HarmonyOS三方件开发指南(5)——Photoview组件
PhotoView使用说明 1. PhotoView功能介绍1.1 组件介绍: PhotoView是一个继承自Image的组件,不同之处在于:它可以进行图击放大功能,手势缩放功能(暂无 ...
- 类转json的基类实现
类转json的基类实现 项目地址 github地址 实现原理 使用反射获取类的属性名和属性内容.具体原理可以自己查一下资料 对一个类调用getClass().getDeclaredFields()可以 ...