2017CCPC网络赛 HDU6158-The Designer 圆的反演

在这里插入图片描述
题意;就如图示一样,两个圆内切,给定你两个圆的半径r1和r2,然后给定你一个n,要求你在这两个圆之间的空隙不断地添加新的圆,使新添加的圆能够,和它相邻的所有圆相切,(包括之前添加的圆)。问你生成的这个n个圆的面积是多少。

在这里插入图片描述
对圆的反演的问题,应该就是围绕着那几个基本性质进行图形的反演变化,多画画图多思考一下。

通过上面的图我们可以看到假如我们把反演中心放在两个圆的最左端有性质我们可以得这两个圆都会经过反演之后都会形成一条直线如图所示的两条黑线左边是由大圆形成的右边由小圆形成然后那些填充的圆经过反演之后有性质可知他们就会在两条直线之间紧密的排列起来如图中的绿色圆组成

然后,我们就从最小的圆 开始反演回去求答案即可。但是注意,可以看到经过数次添加之后,加的圆肯定越来越小,知道某个面积大小时,不会再对答案产生影响时就可以停下循环了,要不TLE qwq。(以后对于double类型的答案并结合大数据是要学会考虑这一种情况,,从而及时的优化时间,但同时也要注意精度对答案的影响,在wa和tle之间找平衡。)

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const double eps = 1e-9;
const int MAXN = 1e7+7;
const double PI = acos(-1.0);

int sign(double x){//判断符号函数
	if(fabs(x) <= eps) return 0;
	if(x > 0) return 1;
	else return -1;
}

struct Point
{
	double x,y;
	Point(){}
	//定义运算 
	Point(double _x,double _y){x = _x;y = _y;}
	Point operator + (const Point &b)const{
		return Point(x+b.x,y+b.y);
	}
	Point operator - (const Point &b)const{
		return Point(x-b.x,y-b.y);
	}
	Point operator * (const double &k)const{//乘常数
		return Point(x*k,y*k);
	}
	Point operator / (const double &k)const{
		return Point(x/k,y/k);
	}
	//点的叉积和点积都是数
	//点积
	double operator * (const Point &b)const{
		return x*b.x+y*b.y;
	}
	//叉积
	double operator ^ (const Point &b)const{
		return x*b.y-y*b.x;
	}

	Point rev(double rad,double r){// rad代表旋转的弧度 r代表圆的半径
    	return Point(x+r*cos(rad),y+r*sin(rad));
    }//求圆上任意一角度的点的坐标

	double powlen(){return x*x+y*y;};

	double len(){return sqrt(powlen());}
};

double cross(Point a,Point b){//叉积
	return a.x*b.y-a.y*b.x;
}

double dot(Point a,Point b){//点积
	return a.x*b.x+a.y*b.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));
}

struct Circle
{
    Point o;
    double r;
    Circle(){}
    Circle(Point o,double r):o(o),r(r){}
};

Circle C1,C2;//反演中心 定义为原点
Point P;
double R;//反演半径

Circle circle_inversion(Circle c1){//圆反演后的圆的半径好圆心的计算公式
	Circle res;
	double oc1 = dis(P,c1.o);
	double k1 = 1.0/(oc1 - c1.r);
	double k2 = 1.0/(oc1 + c1.r);
	res.r = 0.5 * (k1 - k2) * R * R;//反演图形的半径
	double oc2 = 0.5 * (k1 + k2) * R * R;
	res.o = P + (c1.o - P) * oc2 / oc1;//反演图形的圆心
	return res;
}

int main()
{
	R = 10.0;
	int t,n;
	scanf("%d",&t);
	while(t--){
		double r1,r2;
		scanf("%lf%lf",&r1,&r2);
		scanf("%d",&n);
		if(r1 > r2) swap(r1,r2);//用r1表示小圆的半径 
		C1.r = r1,C2.r = r2;
		C1.o.x = r1,C1.o.y = 0;
		C2.o.x = r2,C2.o.y = 0;
		double Lim,Rim;//反演出的两条直线的两个界限
		Lim = R * R / (2.0 * r2);
		Rim = R * R / (2.0 * r1);
		Circle c;
		c.o.x = (Lim + Rim) / 2.0 , c.o.y = 0;
		c.r = (Rim - Lim) / 2.0;
		double ans = 0;
		Circle tmp = circle_inversion(c);
		ans += (tmp.r * tmp.r * PI);
		int cnt = 1;
		Circle uc = {c.o,c.r},dc = {c.o,c.r};//uc上面的圆 dc下面的圆 
		while(cnt < n){
			++cnt;
			if(cnt&1){
				uc.o.y = uc.o.y + uc.r * 2.0;
				tmp = circle_inversion(uc);
				ans += (tmp.r * tmp.r * PI);
			}
			else{
				dc.o.y = dc.o.y - dc.r * 2.0;
				tmp = circle_inversion(dc);
				ans += (tmp.r * tmp.r * PI);
			}
			if(tmp.r*tmp.r*PI * (n-cnt) < 1e-7) break;//这个地方从图里看出 最后的会变得越来越小 因为答案只需要到第五位
			//所以需要设定合理的退出循环的边界条件 我这个代码1e-6WA 1e-7AC 1e-8/1e-9TLE就很玄学
		}
		printf("%.5f\n",ans);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值