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

题目描述

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

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

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

李哲将整棵柠檬树分成了 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-7;
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={0,0,0,0};
    if(fabs(a.r-b.r)<eps)return x={a.x,b.x,0,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=0;
    for(int i=1;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=1;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)+4*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=1;i<=n+1;++i)scanf("%lf",&h[i]);
    for(int i=1;i<=n;++i)scanf("%lf",&r[i]);++n;
    for(int i=1;i<=n;++i){
        h[i]*=alpha;c[i].x=h[i]+c[i-1].x;c[i].r=r[i];
    }    
    for(int i=1;i<n;++i)l[i]=_work(c[i],c[i+1]);
    double L=2e9,R=-2e9;
    for(int i=1;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))*2);
    return 0;
}

 

转载于:https://www.cnblogs.com/ZH-comld/p/10144794.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值