BZOJ1502: [NOI2005]月下柠檬树

12 篇文章 0 订阅
7 篇文章 0 订阅

因为光线平行,射过去的面积其实是一个个圆和他们之间的公切线
算面积可以套自适应Simpson积分,好像大概是这个东西 S(l,r)=rl6(h(l)+h(r)+4h(mid)) S ( l , r ) = r − l 6 ( h ( l ) + h ( r ) + 4 h ( m i d ) )

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 510;
const double eps = 1e-7;
inline double sqr(const double x){return x*x;}

int n; double alp;
struct cir{double xi,ri;}r[maxn]; int rn;
inline bool cover(cir x,cir y)
{
    if(x.ri<y.ri) swap(x,y);
    double dis=fabs(x.xi-y.xi);
    return dis<x.ri-y.ri+eps;
}
struct Seg{int i,j;double alpha,l,r,k,b;}seg[maxn]; int sn;

double f(double x)
{
    double ans=0;
    for(int i=1;i<=rn;i++)
        if(fabs(x-r[i].xi)<r[i].ri+eps) ans=max(ans,sqrt(sqr(r[i].ri)-sqr(x-r[i].xi)));
    for(int i=1;i<=sn;i++)
    {
        int L=seg[i].i,R=seg[i].j;
        double q=seg[i].alpha;
        if(r[L].ri>=r[R].ri)
        {
            double t1=r[L].xi+r[L].ri*sin(q),t2=r[R].xi+r[R].ri*sin(q);
            if(x<=t1||x>=t2) continue;
            double k=(x-t1)/(t2-t1);
            ans=max(ans,r[L].ri*cos(q)-(r[L].ri-r[R].ri)*cos(q)*k);
        }
        else
        {
            double t1=r[L].xi-r[L].ri*sin(q),t2=r[R].xi-r[R].ri*sin(q);
            if(x<=t1||x>=t2) continue;
            double k=(t2-x)/(t2-t1);
            ans=max(ans,r[R].ri*cos(q)-(r[R].ri-r[L].ri)*cos(q)*k);
        }
        //if(seg[i].l<=x&&x<=seg[i].r) ans=max(ans,seg[i].k*x+seg[i].b);
    }
    //printf("%lf %lf\n",x,ans);
    return ans*2.0;
}
double cal(double L,double R)
{
    double fl=f(L),fr=f(R),fm=f((L+R)/2.0);
    return (R-L)/6.0*(fl+fr+fm*4.0);
}
double solve(double L,double R,double now)
{
    double mid=(L+R)/2.0;
    double t1=cal(L,mid),t2=cal(mid,R);
    if(fabs(t1+t2-now)<eps) return now;
    return solve(L,mid,t1)+solve(mid,R,t2);
}

int main()
{
    scanf("%d%lf",&n,&alp); alp=1.0/tan(alp);
    double las=0;
    for(int i=0;i<=n;i++)
    {
        double x; scanf("%lf",&x); x=x*alp;
        las+=x; r[++rn].xi=las;
    }
    for(int i=1;i<rn;i++) scanf("%lf",&r[i].ri);
    r[rn].ri=eps;

    //for(int i=1;i<=rn;i++) printf("%lf %lf\n",r[i].xi,r[i].ri);
    for(int i=1;i<rn;i++)
    {
        if(cover(r[i],r[i+1])) continue;
        ++sn; double q=asin(fabs(r[i+1].ri-r[i].ri)/(r[i+1].xi-r[i].xi));
        seg[sn].i=i,seg[sn].j=i+1,seg[sn].alpha=q;
        /*seg[sn].k=tan(q);
        seg[sn].l=r[i].xi-r[i].ri*sin(q),seg[sn].r=r[i+1].xi-r[i+1].ri*sin(q);
        double h=r[i].ri*cos(q);
        seg[sn].b=h-seg[sn].k*seg[sn].l;
        printf("%lf %lf %lf %lf\n",seg[sn].l,seg[sn].r,seg[sn].k,seg[sn].b);*/
    }
    double ml=0,mr=0;
    for(int i=1;i<=rn;i++) mr=max(mr,r[i].xi+r[i].ri),ml=min(ml,r[i].xi-r[i].ri);
    printf("%.2lf\n",solve(ml,mr,0));

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值