HDU 4773 反演变换

为什么能想到反演,关键就在于这个题的p 是圆上的一点,这就说明这个题目可能用反演去进行变换了。任意规定反演半径和反演基圆(但是要注意精度)然后把两个圆反演出去 (根据结论 不过反演中心的圆反演后还是圆,反演中心是他们的一个位似中心) 然后再求外公切线 (因外题目要求是外切),再把得到的直线反演回来就可以了。

#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;

const double eps = 1e-10;

class Point{
public:
    double x,y;
    Point(double _x=0,double _y=0):x(_x),y(_y){}
    Point operator + (Point  rhs){return Point(x+rhs.x,y+rhs.y);}
    Point operator - (Point  rhs){return Point(x-rhs.x,y-rhs.y);}
    Point operator * (double rhs){return Point(x*rhs,y*rhs);}
    Point operator / (double rhs){return Point(x/rhs,y/rhs);}
    Point Move(double a,double d){return Point(x+d*cos(a),y+d*sin(a));}
    void Read(){scanf("%lf%lf",&x,&y);}
}P;

class Circle{
public:
    Point o;double r;
    Circle(double _x=0,double _y=0,double _r=0):o(_x,_y),r(_r){}
    void Read(){o.Read();scanf("%lf",&r);}
    void out(){printf("%.8f %.8f %.8f\n",o.x,o.y,r);}
}c[5];

int Sign(double x){return (x>eps) - (x<-eps);}
double Cross(Point a,Point b,Point c){return (b.x-a.x)*(c.y-a.y) - (c.x-a.x)*(b.y-a.y);}
double Dis(Point a,Point b){return sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));}
double R;
int tot;

Circle Inverse(Circle a){
    Circle res;
    double oc1 = Dis(P,a.o);
    double k1 = 1.0 / (oc1 - a.r);
    double k2 = 1.0 / (oc1 + a.r);
    res.r = 0.5*(k1 - k2)*R*R;
    double oc2 = 0.5*(k1 + k2)*R*R;
    res.o =P + (a.o - P)*oc2 / oc1;
    return res;
}

void Mark(Point a,Point b){
    ++tot;
    double tem = fabs(Cross(a,P,b)/Dis(a,b))*2.0;
    c[tot].r = R*R/tem;
    double d = Dis(a,c[0].o);
    c[tot].o = P + (a-c[0].o)*(c[tot].r/d);
}

void solve(){
    for(int i=0 ;i<2 ;i++) c[i] = Inverse(c[i]);
    if(c[0].r < c[1].r) swap(c[0],c[1]);
    Point tem = c[1].o - c[0].o;
    double a1 = atan2(tem.y,tem.x);
    double a2 = acos((c[0].r - c[1].r)/Dis(c[0].o,c[1].o));
    Point P1 = c[0].o.Move(a1+a2,c[0].r);
    Point P2 = c[1].o.Move(a1+a2,c[1].r);
    if(Sign(Cross(P1,c[0].o,P2)) == Sign(Cross(P1,P,P2))) Mark(P1,P2);
    P1 = c[0].o.Move(a1-a2,c[0].r);
    P2 = c[1].o.Move(a1-a2,c[1].r);
    if(Sign(Cross(P1,c[0].o,P2)) == Sign(Cross(P1,P,P2))) Mark(P1,P2);
}

int main(){
    R = 5.0; //保证精度的前提下任意取的反演半径
    int T;scanf("%d",&T);
    while(T--){
        tot = 1;
        for(int i=0 ;i<2 ;i++) c[i].Read();
        P.Read();
        solve();
        printf("%d\n",tot-1);
        for(int i=2 ;i<=tot ;i++) c[i].out();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值