【bzoj1502】 NOI2005—月下柠檬树

http://www.lydsy.com/JudgeOnline/problem.php?id=1502 (题目链接)

今天考试题,从来没写过圆的面积之类的东西。。GG

题意:一颗树由n个圆台组成,现在有倾斜角为alpha的光,不计树干阴影,光线沿直线传播,求这个树在水平地面投影的面积。

Solution
  Simpson积分法。
  直接蒯一份题解算了(http://blog.csdn.net/ww140142/article/details/48296273

  给出一颗树。。求这棵树的阴影面积。。。
  什么鬼题!= =
  当然这是一道计算几何;
  首先我们把树的尖看成半径为0的圆;
  三维图形到二维的投影貌似很难啊,不过这题也有很多特殊性;
  因为圆对于平行的面的投影都是相同的正圆,所以可以直接投在地面上;
  而圆心距则是除了一个tan函数的样子,所以圆已经被扔到待求的平面上了;
  这些圆的公切线!(这样就从母的变成公的啦233)
  公切线的求法就是画图上勾股定理相似乱搞,调两组数据改改就好了;
  然后正解似乎是讨论了一堆东西然后分别求面积;
  然后我们似乎可以直接上Simpson积分!
  具体细节不说了,代码里都有;
  这题精度不是特别卡,EPS=1e5就够了?

  当然,我写的是1e7= =。

代码:

// bzoj1502
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<vector>
#define eps 1e-7
#define inf 2147483640
#define LL long long
#define free(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout);
using namespace std;
inline LL getint() {
    LL x=0,f=1;char ch=getchar();
    while (ch>'9' || ch<'0') {if (ch=='-') f=-1;ch=getchar();}
    while (ch>='0' && ch<='9') {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

const int maxn=510;
struct S {double x,y,p,q;}c[maxn];
double h[maxn],s[maxn],r[maxn],alpha,ll,rr;
int n,size=0;

double sqr(double x) {return x*x;}
double f(double l) {
    double t=0.0;
    for (int i=1;i<=n;i++)
        if (fabs(s[i]-l)<r[i]) t=max(t,sqrt(sqr(r[i])-sqr(s[i]-l)));
    for (int i=1;i<=size;i++)
        if (c[i].x<l && l<c[i].p) t=max(t,c[i].y+(c[i].q-c[i].y)*(l-c[i].x)/(c[i].p-c[i].x));
    return t;
}
double Simpson(double l,double r,double fl,double fmid,double fr) {
    double m=(l+r)/2;
    double p=f((l+m)/2),q=f((m+r)/2);
    double x=(fl+4*fmid+fr)*(r-l)/6,y=(fl+4*p+fmid)*(m-l)/6,z=(fmid+4*q+fr)*(r-m)/6;
    if (fabs(x-y-z)<eps) return y+z;
    return Simpson(l,m,fl,p,fmid)+Simpson(m,r,fmid,q,fr);
}
int main() {
    scanf("%d%lf",&n,&alpha);
    alpha=1.0/tan(alpha);
    for (int i=1;i<=n+1;i++) {
        scanf("%lf",&h[i]);
        h[i]+=h[i-1];
        s[i]=h[i]*alpha;
    }
    ll=0;rr=0;
    for (int i=1;i<=n;i++) {
        scanf("%lf",&r[i]);
        ll=min(ll,s[i]-r[i]);
        rr=max(rr,s[i]+r[i]);
    }
    r[n+1]=0;
    for (int i=1;i<=n;i++) {
        double d=s[i+1]-s[i];
        if (d>fabs(r[i]-r[i+1])) {
            c[++size].x=s[i]-r[i]*(r[i+1]-r[i])/d;
            c[size].y=sqrt(sqr(r[i])-sqr(c[size].x-s[i]));
            c[size].p=s[i+1]-r[i+1]*(r[i+1]-r[i])/d;
            c[size].q=sqrt(sqr(r[i+1])-sqr(c[size].p-s[i+1]));
        }
    }
    rr=max(rr,s[n+1]);
    printf("%.2lf",2*Simpson(ll,rr,0,f((ll+rr)/2),0));
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值