题目描述
输入
输出
输出1个实数,表示树影的面积。四舍五入保留两位小数。
样例输入
2 0.7853981633
10.0 10.00 10.00
4.00 5.00
样例输出
171.97
题解
自适应Simpson积分
根据数学知识:在平行光投影下,圆的半径保持不变,位置为 高度/tanα ;圆台/圆锥的侧面投影为两圆/一圆一点的外公切线。
那么先将圆的位置求出,然后依次求相邻两圆公切线并将线段记录,此时注意内切/内含的两个圆是没有外公切线的。这里求公切线使用了射影定理。
如下图:
其中黑色的是投影后的圆,红色的是公切线段。
那么我们要求的就是最外层轮廓所围成的图形的面积。由于对称性因此只需要求出上半部分然后乘2。
注意到上半部分的外层轮廓形成了连续的一段函数,因此考虑使用自适应Simpson积分来求面积。
Simpson积分:对于三次及以下多项式函数,有 $\int_l^rf(x)dx=(r-l)·\frac {f(l)+f(r)+4f(\frac{l+r}2)}6$ 。
自适应Simpson积分:对于任意连续函数 $f(x)$ 求 $\int_l^rf(x)dx$,先将其当作三次函数使用Simpson积分求出 $[l,r]$ 、$[l,mid]$ 和 $[mid+r]$ 的面积,如果左半部分和右半部分面积之和与总面积之差满足精度要求则返回,否则递归左右并求和作为总面积。
本质上相当于插一个三次函数,如果差得比较多则递归左右。
回过头来看本题,问题变为:给出 $x$ ,求 $f(x)$ 。显然可以在每个圆和每条线段上(如果存在)求出 $x$ 的函数值,取最大值即为 $f(x)$ 。
注意精度要设得小一些,因为求和的部分使误差增大。
时间复杂度 $O(跑得过)$ 。
#include <cmath>
#include <cstdio>
#include <algorithm>
#define N 510
#define eps 1e-5
using namespace std;
struct circle
{
double x , r;
}c[N];
struct line
{
double x1 , y1 , x2 , y2;
}l[N];
double h[N];
int n , m;
inline double squ(double x)
{
return x * x;
}
inline double f(double p)
{
int i;
double ans = 0;
for(i = 1 ; i <= n ; i ++ )
if(p >= c[i].x - c[i].r && p <= c[i].x + c[i].r)
ans = max(ans , sqrt(squ(c[i].r) - squ(p - c[i].x)));
for(i = 1 ; i <= m ; i ++ )
if(p >= l[i].x1 && p <= l[i].x2)
ans = max(ans , l[i].y1 + (p - l[i].x1) * (l[i].y2 - l[i].y1) / (l[i].x2 - l[i].x1));
return ans;
}
inline double calc(double l , double r)
{
return (r - l) * (f(l) + f(r) + f((l + r) / 2) * 4) / 6;
}
double simpson(double l , double r , double s)
{
double mid = (l + r) / 2 , x = calc(l , mid) , y = calc(mid , r);
if(fabs(x + y - s) <= eps) return s;
return simpson(l , mid , x) + simpson(mid , r , y);
}
int main()
{
int i;
double alpha , t , L = 1e9 , R = -1e9;
scanf("%d%lf" , &n , &alpha) , n ++ ;
for(i = 1 ; i <= n ; i ++ ) scanf("%lf" , &h[i]) , h[i] += h[i - 1] , c[i].x = h[i] / tan(alpha);
for(i = 1 ; i < n ; i ++ ) scanf("%lf" , &c[i].r);
for(i = 1 ; i < n ; i ++ )
{
if(c[i + 1].x - c[i].x > fabs(c[i + 1].r - c[i].r))
{
m ++ ;
t = c[i].r * (c[i].r - c[i + 1].r) / (c[i + 1].x - c[i].x) , l[m].x1 = c[i].x + t , l[m].y1 = sqrt(squ(c[i].r) - squ(t));
t = c[i + 1].r * (c[i].r - c[i + 1].r) / (c[i + 1].x - c[i].x) , l[m].x2 = c[i + 1].x + t , l[m].y2 = sqrt(squ(c[i + 1].r) - squ(t));
}
}
for(i = 1 ; i <= n + 1 ; i ++ ) L = min(L , c[i].x - c[i].r) , R = max(R , c[i].x + c[i].r);
printf("%.2lf\n" , 2 * simpson(L , R , calc(L , R)));
return 0;
}