Hdu--4773,13杭州D(几何,圆的反演)

2015-02-24 16:06:13

  这道题乍一看能直接数学搞... (就交给MO神犇吧...)

  关于几何反演基础知识,推荐两篇博文:博文1博文2

    从一维反演推广而来的圆反演有蛮多性质,这题我们要关注:

  (1)过反演中心的圆,反形为不过反演中心的直线。    

  (2)不过反演中心的直线,反形为过反演中心的圆。

   例1  例2 

        (图中(0,0)为反演中心,蓝色圆为反演圆,绿色直线和棕色圆互为反形)

  (3)不过反演中心的圆,反形也为不过反演中心的圆。

    

          (图中绿色圆与黄色圆互为反形)

  (4)反演不改变相切性。(定理:相切两圆的反象仍相切,若切点恰是反演中心,则其反象为两平行线)

  其实(1)与(2)互逆,(4)也可证。

  思路:首先我们把给定的P点看做反演中心,然后自定义反演半径的大小(为保证精度,不宜过小),这样我们就得到了反演圆 c(P,R)

        接着,求出题目给出的两个圆c1,c2各自的反形c1’,c2’。

        然后,作出c1’和c2’的外公切线,再将公切线反演回去就得到答案所需的圆了。

      (依据:根据(4)的定理,我们倒着考虑,最终的图形为3个圆,答案圆与另外两圆外切,如果将这三个圆反演回去,答案圆因为过反演中心,所以反形是一条直线,

      另外两个题目给出的圆因为没有过反演中心,所以反形是两个圆,由于相切性不变,这条直线与这两个反形圆均相切,即为公切线。)

      注意:不仅要求的是公切线,为了防止出现内切的情况,我们要求的是“外公切线”,并且要使得P点和反形圆的圆心在外公切线的同一侧。

    

    这个当时很不理解... 所以画了个图,P(0,0),两个大圆相距很近(这里暂时让他们相交,只为说明问题),他们的反形为两个很近的小圆,两条外公切线如图。

     (1)对于红外公切线y=1/6,P点和小圆圆心在公切线异侧,公切线的反形(x^2+(y-3)^2=9)会内切大圆。

     (2)对于绿外公切线y=1/2,P点和小圆圆心在公切线同侧,公切线的反形(x^2+(y-1)^2=1)会外切大圆。

    由此,当P点和反形圆的圆心在外公切线的同一侧时,该外公切线才是符合要求的。

    最后,将公切线反演回去就比较方便了,求出P点到直线距离,然后就可就出答案圆的半径,至于答案圆的圆心可以用向量相似解决。

     (A了好久... 如有错误,还望指出)

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <cstdlib>
  4 #include <cmath>
  5 #include <vector>
  6 #include <map>
  7 #include <set>
  8 #include <stack>
  9 #include <queue>
 10 #include <string>
 11 #include <iostream>
 12 #include <algorithm>
 13 using namespace std;
 14 
 15 #define MEM(a,b) memset(a,b,sizeof(a))
 16 #define REP(i,n) for(int i=1;i<=(n);++i)
 17 #define REV(i,n) for(int i=(n);i>=1;--i)
 18 #define FOR(i,a,b) for(int i=(a);i<=(b);++i)
 19 #define RFOR(i,a,b) for(int i=(a);i>=(b);--i)
 20 #define getmid(l,r) ((l) + ((r) - (l)) / 2)
 21 #define MP(a,b) make_pair(a,b)
 22 
 23 typedef long long ll;
 24 typedef pair<int,int> pii;
 25 const int INF = (1 << 30) - 1;
 26 const double eps = 1e-10;
 27 
 28 double add(double a,double b){ //考虑误差的浮点加法
 29     if(abs(a + b) < eps * (abs(a) + abs(b))) return 0;
 30     return a + b;
 31 }
 32 
 33 struct Point{
 34     double x,y;
 35     Point(double tx = 0,double ty = 0) : x(tx),y(ty) {}
 36     Point operator + (Point p){
 37         return Point(add(x,p.x),add(y,p.y));
 38     }
 39     Point operator - (Point p){
 40         return Point(add(x,-p.x),add(y,-p.y));
 41     }
 42     Point operator * (double d){
 43         return Point(x * d,y * d);
 44     }
 45     Point operator / (double d){
 46         return Point(x / d,y / d);
 47     }
 48     Point Move(double a,double d){
 49         return Point(x + d * cos(a),y + d * sin(a));
 50     }
 51     void Read(){
 52         scanf("%lf%lf",&x,&y);
 53     }
 54 };
 55 
 56 struct Circle{
 57     Point o;
 58     double r;
 59     Circle(double tx = 0,double ty = 0,double tr = 0) : o(tx,ty),r(tr) {}
 60     void Read(){
 61         o.Read();
 62         scanf("%lf",&r);
 63     }
 64     void Out(){
 65         printf("%.8f %.8f %.8f\n",o.x,o.y,r);
 66     }
 67 };
 68 
 69 int Sign(double x){ //判断x的正负
 70     return (x > eps) - (x < -eps);
 71 }
 72 
 73 double Cross(Point a,Point b,Point c){ //叉积
 74     return (b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y);
 75 }
 76 
 77 double Dis(Point a,Point b){
 78     return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
 79 }
 80 
 81 Circle c[5];
 82 Point P;
 83 int T,tot;
 84 double R;
 85 
 86 Circle Inver(Circle c1){ //反演过程
 87     Circle res;
 88     double oc1 = Dis(P,c1.o);
 89     double k1 = 1.0 / (oc1 - c1.r);
 90     double k2 = 1.0 / (oc1 + c1.r);
 91     res.r = 0.5 * (k1 - k2) * R * R;
 92     double oc2 = 0.5 * (k1 + k2) * R * R;
 93     res.o = P + (c1.o - P) * oc2 / oc1;
 94     return res;
 95 }
 96 
 97 void Mark(Point a,Point b){ //记下答案圆
 98     ++tot;
 99     double t = fabs(Cross(a,P,b) / Dis(a,b)); //求出P点到直线的距离
100     c[tot].r = R * R / (2.0 * t);
101     double d = Dis(a,c[1].o);
102     c[tot].o = P + (a - c[1].o) * (c[tot].r / d); //因为向量(a,c[1].o)与公切线垂直,所以可以利用其长度相似出P到c[tot]圆心的距离
103 }
104 
105 void Solve(){
106     REP(i,2) c[i] = Inver(c[i]); //将已知两圆反演
107     if(c[1].r < c[2].r) swap(c[1],c[2]); //c[1]圆的半径较大
108     Point tmp = c[2].o - c[1].o;
109     double a1 = atan2(tmp.y,tmp.x); //atan2的经典应用,求出直线的倾斜角!
110     double a2 = acos((c[1].r - c[2].r) / Dis(c[1].o,c[2].o));
111     Point P1 = c[1].o.Move(a1 + a2,c[1].r);
112     Point P2 = c[2].o.Move(a1 + a2,c[2].r);
113     if(Sign(Cross(P1,c[1].o,P2)) == Sign(Cross(P1,P,P2))) Mark(P1,P2); //保证P与c[1]圆心在公切线同侧
114     P1 = c[1].o.Move(a1 - a2,c[1].r); //同样要考虑公切线在下(上)方的情况
115     P2 = c[2].o.Move(a1 - a2,c[2].r);
116     if(Sign(Cross(P1,c[1].o,P2)) == Sign(Cross(P1,P,P2))) Mark(P1,P2);
117 }
118 
119 int main(){
120     R = 10.0; //自定义的反演半径
121     scanf("%d",&T);
122     REP(tt,T){
123         tot = 2;
124         REP(i,2) c[i].Read();
125         P.Read();
126         Solve();
127         printf("%d\n",tot - 2);
128         FOR(i,3,tot) c[i].Out();
129     }
130     return 0;
131 }

 

posted @ 2015-02-24 16:15  Naturain  阅读(1523)  评论(3编辑  收藏  举报