Luogu P4207 [NOI2005]月下柠檬树

Link
可以发现投影是一些圆,相邻的两个圆之间有一个由两个外公切线构成的梯形。
不难发现投影关于(x)轴对称,因此只需要求出上半部分的面积。
那么我们先求出所有相邻两圆的外公切线中上方的一条,然后自适应Simpson积分即可。

#include<cmath>
#include<cstdio>
#include<algorithm>
typedef double db;
const int N=507;const db eps=1e-10,pi=acos(-1);
int n,m;db t,L=1e10,R=-1e10;
struct line
{
    db l,r,k,b;
    void init(db x1,db y1,db x2,db y2){l=x1,r=x2,k=(y2-y1)/(x2-x1),b=y1-k*x1;}
    db f(db x){return k*x+b;}
}l[N];
struct circle{db x,r;}c[N];
db f(db x)
{
    db h=0;
    for(int i=1;i<=m;++i) if(l[i].l-eps<x&&x<l[i].r+eps) h=std::max(h,l[i].f(x));
    for(int i=1;i<=n;++i)
    {
	db d=fabs(x-c[i].x);
	if(d-eps<c[i].r) h=std::max(h,sqrt(c[i].r*c[i].r-d*d));
    }
    return h;
}
db g(db l,db r){return (f(l)+f(r)+4*f((l+r)/2))/6*(r-l);}
db cal(db l,db r){db v=g(l,r),mid=(l+r)/2;return fabs(g(l,mid)+g(mid,r)-v)<eps? v:cal(l,mid)+cal(mid,r);}
int main()
{
    scanf("%d%lf",&n,&t),t=1/tan(t),++n;
    for(int i=1;i<=n;++i) scanf("%lf",&c[i].x),c[i].x=c[i-1].x+c[i].x*t;
    for(int i=1;i<n;++i) scanf("%lf",&c[i].r);
    for(int i=1;i<=n;++i) L=std::min(L,c[i].x-c[i].r),R=std::max(R,c[i].x+c[i].r);
    for(int i=1;i<n;++i)
    {
	db len=c[i+1].x-c[i].x,si,co;
	if(len+eps>fabs(c[i+1].r-c[i].r)) si=(c[i].r-c[i+1].r)/len,co=sqrt(1-si*si),l[++m].init(c[i].x+c[i].r*si,c[i].r*co,c[i+1].x+c[i+1].r*si,c[i+1].r*co);
    }
    printf("%.2lf",2*cal(L,R));
}
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12342822.html