[bzoj1502][NOI2005]月下柠檬树——自适应simpson积分

题目大意:

给定一个由圆台构成的树以及平行光线的夹角,求树的阴影面积。

思路:

一个圆台在平行光下投影必定是两个圆加上两条外公切线,题目所求即n个这种图形的面积并。
每个圆的面积和相邻两个圆的公切线可以利用三角函数来求解,考虑如何求解这个不规则图形的面积。
可以按照圆心连线为x轴建系,可以对于在x轴上的部分求解积分,不难发现这个不规则图形的大部分都是规则图形,于是直接自适应simpson积分即可。
注意几个细节,左右端点的选取要注意,以及尽量避免计算重复的值。

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a;i<=b;++i)
typedef long long ll;

using namespace std;

void File(){
    freopen("bzoj1502.in","r",stdin);
    freopen("bzoj1502.out","w",stdout);
}

const double pi=acos(-1.0);
const int maxn=500+10;
int n;
double a,x[maxn],r[maxn],a1[maxn],a2[maxn],k[maxn],b[maxn];
bool is[maxn];

double geth(double p,int i){
    if(p<a1[i] || p>a2[i])return 0;
    return k[i]*p+b[i];
}

double fun(double p){
    double ret=0;
    REP(i,1,n+1)if(fabs(x[i]-p)<=r[i])
        ret=max(ret,sqrt(r[i]*r[i]-(x[i]-p)*(x[i]-p)));
    REP(i,1,n)ret=max(ret,geth(p,i));
    return ret;
}

double simpson(double L,double R,double lv,double mv,double rv){
    return (R-L)/6*(lv+4*mv+rv);
}

double solve(double L,double R,double eps,double lv,double mv,double rv){
    double mid=(L+R)/2,ss=fun((L+mid)/2),tt=fun((mid+R)/2);
    double s0=simpson(L,R,lv,mv,rv);
    double s1=simpson(L,mid,lv,ss,mv);
    double s2=simpson(mid,R,mv,tt,rv);
    if(fabs(s1+s2-s0)<=15*eps)return s1+s2+(s1+s2-s0)/15;
    return solve(L,mid,eps,lv,ss,mv)+solve(mid,R,eps,mv,tt,rv);
}

void init(){
    scanf("%d%lf",&n,&a);
    double h=0,t,ta=1.0/tan(a);
    REP(i,1,n+1){
        scanf("%lf",&t);
        h+=t;
        x[i]=ta*h;
    }
    REP(i,1,n)scanf("%lf",&r[i]);
    REP(i,1,n){
        double c=abs(r[i+1]-r[i])/(x[i+1]-x[i]);
        c=pi/2-asin(c);
        double si=sin(c),co=cos(c);
        double b1=si*r[i],b2=si*r[i+1];
        a1[i]=x[i]+(r[i]<r[i+1] ? -1 : 1)*co*r[i];
        a2[i]=x[i+1]+(r[i]<r[i+1] ? -1 : 1)*co*r[i+1];
        k[i]=(b1-b2)/(a1[i]-a2[i]);
        b[i]=b1-k[i]*a1[i];
    }
}

int main(){
    File();
    init();
    double L=999999999,R=-999999999;
    REP(i,1,n+1)L=min(L,x[i]-r[i]),R=max(R,x[i]+r[i]);
    printf("%.2lf\n",solve(L,R,1e-8,fun(L),fun((L+R)/2),fun(R))*2);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值