题意;就如图示一样,两个圆内切,给定你两个圆的半径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; }