HDOJ 6158

题目

给定2个内切的大圆,半径分别为$R_1,R_2$,按下图顺序依次画上相切的小圆,问前$N$个小圆的面积是多少。

img

数据范围

$Case=1200\quad 1\leq N \leq 10^7$

做法

反演,推公式,便可$O(1)$得到每个小圆的半径。

但题目有1200个Case,每个Case要算最多$10^7$个小圆半径,直接算会超时。

发现:小圆半径递减得很快,而且题目只要求$10^5$的精度,于是每个Case不需要把$N$个全都算出来,后面的在当前精度下对答案不产生影响就不用算了。不产生影响的必要条件是:把当前圆的面积当作后面每个圆的面积,求和,若都不对答案产生影响,则后面的圆就不用算了。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <bits/stdc++.h>
using namespace std;
const double EPS = 1e-7;
const double PI = acos(-1.0);
double R1, R2;
int N;
inline double getR(int n)
{
double k1 = 4 * R1 * R2 / (R1 - R2);
double k2 = (R1 + R2) / (R1 - R2);
return k1 / (k2 * k2 + ((double)n * n - 1));
}
void solve()
{
double tmpR = R1 - R2;
double ans = tmpR * tmpR;
if (N % 2 == 0) {
tmpR = getR(N);
ans += tmpR * tmpR;
}
for (int i = 2; i < N; i += 2) {
tmpR = getR(i);
ans += tmpR * tmpR * 2;
if (tmpR * tmpR * PI * (N - i) < EPS) break;
}
ans *= PI;
printf("%.5f\n", ans);
}
int main()
{
// freopen("/home/linjian/Documents/in.txt", "r", stdin);
int T; scanf("%d", &T);
while (T--) {
scanf("%lf%lf%d", &R1, &R2, &N);
if (abs(R1 - R2) < (1e-7)) {
printf("0.00000\n");
continue;
}
if (R1 < R2) swap(R1, R2);
solve();
}
return 0;
}

注意

  1. 特判2个半径相等的情况,此时输出不是0,是0.00000