[NOI2005]月下柠檬树(计算几何+积分)

时间:2021-10-12 16:12:58

题目描述

李哲非常非常喜欢柠檬树,特别是在静静的夜晚,当天空中有一弯明月温柔 地照亮地面上的景物时,他必会悠闲地坐在他亲手植下的那棵柠檬树旁,独自思 索着人生的哲理。

李哲是一个喜爱思考的孩子,当他看到在月光的照射下柠檬树投在地面上的 影子是如此的清晰,马上想到了一个问题:树影的面积是多大呢?

李哲知道,直接测量面积是很难的,他想用几何的方法算,因为他对这棵柠 檬树的形状了解得非常清楚,而且想好了简化的方法。

李哲将整棵柠檬树分成了 n 层,由下向上依次将层编号为 1,2,…,n。从第 1 到 n-1 层,每层都是一个圆台型,第 n 层(最上面一层)是圆锥型。对于圆台型, 其上下底面都是水平的圆。对于相邻的两个圆台,上层的下底面和下层的上底面 重合。第 n 层(最上面一层)圆锥的底面就是第 n-1 层圆台的上底面。所有的底面 的圆心(包括树顶)处在同一条与地面垂直的直线上。李哲知道每一层的高度为 h1,h2,…,hn,第 1 层圆台的下底面距地面的高度为 h0,以及每层的下底面的圆的 半径 r1,r2,…,rn。李哲用熟知的方法测出了月亮的光线与地面的夹角为 alpha。

题解

脑补一下这个影子长什么样子(反正我脑补不出来),是一堆圆和相邻两个圆之间构成的梯形构成,我们要求的就是这一大坨东西的面积。

只需要保留两位小数,我们可以用自适应辛普森积分来求。

然后注意高度要乘上三角函数,但半径不用,因为平面在竖直方向的投影长度为零。

然后就是用计算几何乱搞(好恶心)。

细节还是挺多的,可能是我太菜了,斜边和直角边都搞错了。

代码

#include<iostream>
#include<cstdio>
#include<cmath>
#define N 602
using namespace std;
const double eps=1e-;
int n;
double alpha,h[N],r[N];
struct yuan{double x,r;}c[N];
struct xianduan{double l,r,k,b;}l[N];
inline xianduan _work(yuan a,yuan b){
xianduan x;
if((a.x-a.r<=b.x-b.r&&a.x+a.r>=b.x+b.r)||(b.x-b.r<=a.x-a.r&&b.x+b.r>=a.x+a.r))return x={,,,};
if(fabs(a.r-b.r)<eps)return x={a.x,b.x,,a.r};
double r1=fabs(a.r-b.r),x1=b.x-a.x,zh=sqrt(x1*x1-r1*r1),co=zh/x1,si=r1/x1,x2=a.r/co,x3=b.r/co;
x.k=(x3-x2)/x1;x.b=x2-a.x*x.k;
if(a.r>b.r)x.l=a.x+a.r*si,x.r=b.x+b.r*si;else x.l=a.x-a.r*si,x.r=b.x-b.r*si;
return x;
}
inline double F(double x){
double ans=;
for(int i=;i<=n;++i)if(c[i].x-c[i].r<=x&&c[i].x+c[i].r>=x)//勾股定理
ans=max(ans,sqrt(c[i].r*c[i].r-(x-c[i].x)*(x-c[i].x)));
for(int i=;i<n;++i)if(l[i].l<=x&&l[i].r>=x)ans=max(ans,x*l[i].k+l[i].b);
return ans;
}
inline double simpson(double l,double r){
return (F(l)+F(r)+*F((l+r)/2.0))*(r-l)/6.0;
}
double solve(double l,double r,double num){
double mid=(l+r)/2.0,x=simpson(l,mid),y=simpson(mid,r);
if(fabs(x+y-num)<eps)return num;
else return solve(l,mid,x)+solve(mid,r,y);
}
int main(){
scanf("%d%lf",&n,&alpha);alpha=1.0/tan(alpha);
for(int i=;i<=n+;++i)scanf("%lf",&h[i]);
for(int i=;i<=n;++i)scanf("%lf",&r[i]);++n;
for(int i=;i<=n;++i){
h[i]*=alpha;c[i].x=h[i]+c[i-].x;c[i].r=r[i];
}
for(int i=;i<n;++i)l[i]=_work(c[i],c[i+]);
double L=2e9,R=-2e9;
for(int i=;i<=n;++i){
L=min(L,c[i].x-c[i].r);
R=max(R,c[i].x+c[i].r);
}
printf("%.2lf",solve(L,R,simpson(L,R))*);
return ;
}