【 HDU4773 】Problem of Apollonius (圆的反演)
BUPT2017 wintertraining(15) #5G
HDU - 4773 - 2013 Asia Hangzhou Regional Contest problem D
题意
给定两个相离的圆,和一个圆外的点P,求过该点和两个圆都外切的圆。
题解
直接求解联立的方程组不太可行。需要用一个黑科技——圆的反演。
什么是圆的反演呢?
假设定圆的圆心为O,半径是R,线段OP上的点P'满足\(|OP|\cdot|OP'|=R^2\),则称P'是P关于定圆O的反演。
反演的性质:
- 不通过O的直线反演后为通过O的圆
- 不通过O的圆反演后变成不通过O的圆
- 圆C与其反演后的圆C'的切线再反演成的圆C1相切
于是这题就可以 以P为反演中心,反演半径为1,将两个圆反演变换为新的两个圆,将新的两个圆的外公切线求出来,其中 P与圆心 都在该切线同侧的切线 关于P反演变换的圆 就是符合题意的。因为如果是在切线两侧就是内切,如下图的黑色切线,P点和两个新的圆的圆心在其两侧,则它的反演的圆将内切C1,C2,题目要我们求的是外切的。红色的切线反演的圆就是C3。
(顺便,画图工具扔一下:Desmos)
现在的问题是如何求反演和外公切线。
利用圆上和p最近的点及最远的点可以求出对应的反演点,它们的距离就是直径,它们的中点就是圆心,或者圆心可以利用三角函数求得。
外公切线,参照白书P267写的。
可以根据下面代码画图理解一下。
代码
#include <cstdio>
#include <algorithm>
#define dd double
#define eps 1e-10
using namespace std;
dd sqr(dd x){return x*x;}
struct cir{
dd x,y,r;
cir(dd _x=0,dd _y=0,dd _r=0):x(_x),y(_y),r(_r){}
void in(int t){scanf("%lf%lf",&x,&y);if(t)scanf("%lf",&r);}
void out(){printf("%f %f %f\n",x,y,r);}
cir point(dd a){//以圆心为原点,a为极角,对应的圆上的点。
return cir(x+r*cos(a),y+r*sin(a));
}
}p,c1,c2,st[5],ed[5];
int cnt;
dd xmult(cir a,cir b,cir o){
return (a.x-o.x)*(b.y-o.y)-(a.y-o.y)*(b.x-o.x);
}
dd dis(cir a,cir b,cir c){
dd A=b.y-a.y,B=a.x-b.x,C=b.x*a.y-b.y*a.x;
return fabs(A*c.x+B*c.y+C)/sqrt(sqr(A)+sqr(B));
}
dd dis(cir a,cir b){
return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y));
}
cir cross(dd a1,dd b1,dd c1,dd a2,dd b2,dd c2){//a1X+b1Y+c1=0和a2X+b2Y+c2=0的交点
dd y=-c1/b1;
if(a1==0)return cir((-c2-b2*y)/a2,y);
y=(a2*c1/a1-c2)/(b2-b1*a2/a1);
return cir(-(c1+b1*y)/a1,y);
}
void inv(cir &c){//圆c反演变换
dd d=dis(c,p),s=sqr(p.r)/(d-c.r),t=sqr(p.r)/(d+c.r);
c.r=(s-t)/2;
c.x=p.x+(c.x-p.x)/d*(t+c.r);
c.y=p.y+(c.y-p.y)/d*(t+c.r);
}
cir inv(cir a,cir b){//直线ab的反演
dd a1=b.y-a.y,b1=a.x-b.x,c1=a.y*b.x-a.x*b.y;//直线ab写成a1X+b1Y+c=0的形式
cir cr=cross(a1,b1,c1,b1,-a1,a1*p.y-b1*p.x);//p到直线ab的垂足
dd r=sqr(p.r)/dis(a,b,p)/2,d=dis(cr,p);
return cir(p.x+r/d*(cr.x-p.x),p.y+r/d*(cr.y-p.y),r);
}
int sgn(dd a){
return (a>eps)-(a<-eps);
}
bool sameside(cir a,cir b,cir s,cir t){
return sgn(xmult(s,t,a))==sgn(xmult(s,t,b));//利用叉积判断是否在直线同侧
}
void tangent(cir a,cir b){
cnt=0;
dd base=atan2(b.y-a.y,b.x-a.x),d=dis(a,b),ang=acos((a.r-b.r)/d);
//这里因为写成a.y-b.y,a.x-b.x而wa了,画了下图就明白了
st[cnt]=a.point(base-ang),ed[cnt]=b.point(base-ang);
if(sameside(p,a,st[cnt],ed[cnt]))cnt++;//p和圆心在切线的同侧
st[cnt]=a.point(base+ang),ed[cnt]=b.point(base+ang);
if(sameside(p,a,st[cnt],ed[cnt]))cnt++;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
c1.in(1);c2.in(1);p.in(0);p.r=1;
inv(c1);inv(c2);//c1,c2关于p反演
tangent(c1,c2);//求外公切线
printf("%d\n",cnt);
for(int i=0;i<cnt;i++)inv(st[i],ed[i]).out();//外公切线关于p反演后的圆
}
return 0;
}
【 HDU4773 】Problem of Apollonius (圆的反演)的更多相关文章
- HDU-4773 Problem of Apollonius (圆的反演)
参考: https://oi-wiki.org/geometry/inverse/ https://blog.csdn.net/acdreamers/article/details/16966369 ...
- Pick定理、欧拉公式和圆的反演
Pick定理.欧拉公式和圆的反演 Tags:高级算法 Pick定理 内容 定点都是整点的多边形,内部整点数为\(innod\),边界整点数\(ednod\),\(S=innod+\frac{ednod ...
- 圆的反演变换(HDU4773)
题意:给出两个相离的圆O1,O2和圆外一点P,求构造这样的圆:同时与两个圆相外切,且经过点P,输出圆的圆心和半径 分析:画图很容易看出这样的圆要么存在一个,要么存在两个:此题直接解方程是不容易的,先看 ...
- hdu 4773 Problem of Apollonius
莫名其妙就AC了-- 圆的反演-- 神马是反演? 快去恶补奥数-- #include<iostream> #include<map> #include<string> ...
- 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 题意: ...
- 【HDU】4773 Problem of Apollonius
题意 给定相离的两个圆(圆心坐标以及半径)以及圆外的一个定点\(P\),求出过点\(P\)的且与已知的两个圆外切的所有圆(输出总数+圆心.半径). 分析 如果强行解方程,反正我是不会. 本题用到新姿势 ...
- 「HDU6158」 The Designer(圆的反演)
题目链接多校8-1009 HDU - 6158 The Designer 题意 T(<=1200)组,如图在半径R1.R2相内切的圆的差集位置依次绘制1,2,3,到n号圆,求面积之和(n< ...
- CF77E Martian Food(圆的反演or 笛卡尔定理+韦达定理)
题面 传送门 这题有两种方法(然而两种我都想不到) 方法一 前置芝士 笛卡尔定理 我们定义一个圆的曲率为\(k=\pm {1\over r}\),其中\(r\)是圆的半径 若在平面上有两两相切,且六个 ...
随机推荐
- Tarjan算法(缩点)
因为最近在学2sat,需要学习前置技能—Tarjan算法,所以花了一天的时间学习这个算法 算法步骤: 1.从一个点开始dfs,并加入栈 2.如果下一个点没有到过,跳到第一步 3.如果下一个点到过,并且 ...
- Python学习第十一篇——for 的本质及如何正确修改列表
假如现在有一个列表:magicians_list = ['mole','jack','lucy'],现在想通过一个函数来实现,在列表的每个元素前面加上“the Great”的字样.现在通过一个函数来实 ...
- JS_左边栏菜单
需求: 要求实现左边栏菜单点击一下就弹开,其他的隐藏.再点击一下就隐藏. 最多只能有一个菜单的详细内容会显示出来. 三个菜单实现联动效果. 代码如下: 1 <!DOCTYPE html> ...
- 练习MD5加密jar包编写
简介 参数签名可以保证开发的者的信息被冒用后,信息不会被泄露和受损.原因在于接入者和提供者都会对每一次的接口访问进行签名和验证. 签名sign的方式是目前比较常用的方式. 第1步:接入者把需求访问的接 ...
- Python3练习题 022:用递归函数反转字符串
方法一 str = input('请输入若干字符:') def f(x): if x == -1: return '' else: return s ...
- GRASP软件设计的模式和原则
GRASP 模式:每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心.”这是关于模式最经典的定义,作者是建筑大师Christopher Alexander.如果是第一次看到这 ...
- java集合迭代器
一.Java中有一个设计模式是迭代器模式 1.迭代器模式定义迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示. 2.迭代器模式概述Java集合框 ...
- Spring在web开发中的应用
(1)在 web 项目中要使用 spring 需要导入一个 jar 包: spring-web-4.2.4.jar包 (2)在 web.xml 文件中配置 Listener <listener& ...
- Day 6-2简单的socket通信
什么是socket? Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面 ...
- 记一次ntp反射放大ddos攻击
2018/3/26 ,共计310G左右的DDoS攻击 临时解决办法:将web服务转移到同生产一个内网段的备份服务器a上,a提供web端口80,数据库通过内网连接还是沿用生产数据库. 后续解决办法:通过 ...