P4207题解

· · 题解

蒟蒻硬肝了两天才肝懂这道题,我太难了~~~

题目大意

求几个在竖直方向连续的圆台和圆锥在仰角为 \alpha 光线照射下,水平地面上的影子的面积。

分析

1.

由于这个影子可能很奇怪(复杂),所以考虑积分,即 Simpson 积分法,这里就不展开了。

2.

首先求出投影下来要变化的量,思考一下,可以发现只有 h 的值投影下来发生了变化,如下图所示:

导一下角,用三角函数即可求出投影下来的长度。

3.

考虑一下圆台的阴影怎么求。因为圆台的两个圆与地面平行(都为水平),所以它们会在地上投影出两个一模一样的圆,圆心的间距 l= 圆台的高度 *\cot\alpha ,像这样:

因为我们要求积分,所以得先求解析式,才能套板板,于是得用初中知识--相似

核心推导都在图里,其它的边可以用比例和勾股定理求出,求得 C , D 点坐标,最后求出解析式,保存一下。

4.

若两个大圆中间夹着一个小圆,它们的公切线就会相交,需要在求积分的时候注意一下,取最大值。

若两个小圆中间夹着一个大圆,它们的公切线中间就会出现一段圆弧,需要在求积分的时候用勾股定理计算。

若一个大圆包着一个小圆,也是求最大值。

注意最顶上的圆锥可以视为一个圆面的半径为 0 的圆台。

Code

#include <bits/stdc++.h>
#define jd 1e-7
#define D double
#define max(x,y) (x>y?x:y)
#define min(x,y) (x<y?x:y)
#define fr(x,y,p) for(int x=y;p;++x)
#define rf(x,y,p) for(int x=y;p;--x)
#define abs(x)(x>0?x:-(x))//这里的-(x)要打括号!!!
using namespace std;
struct cir{
    D x,r;
}a[505];
struct cut_line{//cir[i]->cir[i+1]切线 
    D k,b,l,r;
}p[505];
int n;
D alpha;
void calc_cut_line(int x,int y){
    if(abs(a[x].r-a[y].r)<=jd){
        p[x].k=0,p[x].b=a[x].r,p[x].l=a[y].x,p[x].r=a[y].x;
        return;
    }
    if(a[x].r>a[y].r){
        D ac=a[y].x-a[x].x,aj=a[x].r-a[y].r,S_ckh=aj/ac,ch=a[x].r*S_ckh,ag=a[y].r*S_ckh,kh=sqrt(a[x].r*a[x].r-ch*ch),lg=sqrt(a[y].r*a[y].r-ag*ag);//推导过程 
        p[x].k=(lg-kh)/(ac-ch+ag),p[x].b=lg-(a[y].x+ag)*p[x].k,p[x].l=a[x].x+ch,p[x].r=a[y].x+ag;
    }else{
        D ac=a[y].x-a[x].x,aj=a[y].r-a[x].r,S_ckh=aj/ac,ch=a[y].r*S_ckh,ag=a[x].r*S_ckh,kh=sqrt(a[y].r*a[y].r-ch*ch),lg=sqrt(a[x].r*a[x].r-ag*ag);//推导过程 
        p[x].k=(kh-lg)/(ac-ch+ag),p[x].b=lg-(a[x].x-ag)*p[x].k,p[x].l=a[x].x-ag,p[x].r=a[y].x-ch;
    }
}
D f(D x){
    D ans=0;//求已知的对应点y的最大值 
    fr(i,1,i<=n){//与圆的交点 
        if(x>a[i].x-a[i].r&&x<a[i].x+a[i].r)//取不取等看心情 
        ans=max(ans,sqrt(a[i].r*a[i].r-(a[i].x-x)*(a[i].x-x)));
    }
    fr(i,1,i<n){
        if(x>=p[i].l&&x<=p[i].r)
        ans=max(ans,p[i].k*x+p[i].b);
    }
    return ans;
}
D Simpson(D l,D r){
    return (r-l)*(f(r)+f(l)+f((r+l)/2)*4)/6;
}
D jf(D l,D r,D sum){
    D mid=(l+r)/2,le=Simpson(l,mid),ri=Simpson(mid,r);
    if(abs(le+ri-sum)<=jd) return le+ri;
    else return jf(l,mid,le)+jf(mid,r,ri);
}
int main() {
    scanf("%d %lf",&n,&alpha);
    alpha=1/tan(alpha),a[++n].r=0;
    fr(i,1,i<=n){
        scanf("%lf",&a[i].x);
        a[i].x*=alpha;
        if(i>0) a[i].x+=a[i-1].x;
    }
    fr(i,1,i<n) scanf("%lf",&a[i].r);
    fr(i,1,i<n) calc_cut_line(i,i+1);
    D l,r;
    fr(i,1,i<=n) l=min(l,a[i].x-a[i].r),r=max(r,a[i].x+a[i].r);
    printf("%.2lf",2*jf(l,r,Simpson(l,r))); 
    return 0;
}