luogu P4207 [NOI2005]月下檸檬樹

luogu

爲了方便,設樹頂的那一點爲一個半徑爲0的圓

首先我們把所有圓投影到平面上,然後得到的圖形應該是一堆圓心在\(x\)軸上的圓+相鄰兩個圓公切線構成的梯形的並,我們可以只計算上面一半的面積然後乘2.因爲上面一半的輪廓線不規則,考慮自適應Simpson求輪廓線和\(x\)軸圍成的面積.現在的問題是求出某一個橫座標上與\(x\)軸垂直的直線和輪廓線的交點縱座標,因爲這個輪廓線是圓和公切線的並,那麼我們只要枚舉所有圓和公切線,然後對於這些圖形在\(x\)處的縱座標取最大值即可

至於圓的公切線,這可以把兩個圓的半徑同時減去較小的半徑,然後就變成圓和點的切線,可以用一些\(\sin\cos\)等算出來,最後平移回原位置即可

#include<bits/stdc++.h>
#define LL long long
#define db double

using namespace std;
const int N=500+10;
const db pi=acos(-1),eps=1e-10;
int rd()
{
    int x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+(ch^48);ch=getchar();}
    return x*w;
}
int n;
db gg,h[N],r[N],li[N][2],ps[N][2];
db sq(db x){return x*x;}
db f(db x)
{
    db an=0;
    for(int i=1;i<n;++i)
    if(x>ps[i][0]-eps&&x<ps[i][1]+eps) an=max(an,li[i][0]*x+li[i][1]);
    for(int i=1;i<=n;++i)
    {
        if(fabs(x-h[i])>r[i]-eps) continue;
        an=max(an,sqrt(sq(r[i])-sq(fabs(x-h[i]))));
    }
    return an;
}
db g(db l,db r){return (f(l)+4*f((l+r)/2)+f(r))/6*(r-l);}
db cal(db l,db r,db s)
{
    db mid=(l+r)/2,s1=g(l,mid),s2=g(mid,r);
    if(fabs(s-s1-s2)<eps) return s;
    return cal(l,mid,s1)+cal(mid,r,s2);
}

int main()
{
//be careful of details
    n=rd()+1;
    scanf("%lf",&gg),gg=1/tan(gg);
    for(int i=1;i<=n;++i) scanf("%lf",&h[i]),h[i]=h[i]*gg+h[i-1];
    for(int i=1;i<n;++i) scanf("%lf",&r[i]);
    for(int i=1;i<n;++i)
    {
        if(fabs(r[i]-r[i+1])<eps)
        {
            li[i][0]=0,li[i][1]=r[i],ps[i][0]=h[i],ps[i][1]=h[i+1];
            continue;
        }
        db nx=0,ny=abs(r[i]-r[i+1]),xx=nx,yy=ny;
        db a1=acos(ny/(h[i+1]-h[i])),ag=pi/2-a1,nk,nb;
        if(r[i]>r[i+1])
        {
            nx=xx*cos(ag)+yy*sin(ag),ny=yy*cos(ag)-xx*sin(ag);
            nk=-ny/(h[i+1]-h[i]-nx),nb=-nk*h[i+1]+r[i+1]*(sin(a1)-nk*sin(ag));
        }
        else
        {
            nx=xx*cos(ag)-yy*sin(ag),ny=yy*cos(ag)+xx*sin(ag);
            nk=ny/(h[i+1]+nx-h[i]),nb=-nk*h[i]+r[i]*(sin(a1)+nk*sin(ag));
        }
        db dx=r[i]*sin(ag)*(r[i]<r[i+1]?-1:1),d2=r[i+1]*sin(ag)*(r[i]<r[i+1]?-1:1);
        li[i][0]=nk,li[i][1]=nb;
        if(fabs(min(r[i],r[i+1]))>eps) ps[i][0]=h[i]+dx,ps[i][1]=h[i+1]+d2;
        else if(fabs(r[i])<eps) ps[i][0]=h[i],ps[i][1]=h[i+1]+nx;
        else ps[i][0]=h[i]+nx,ps[i][1]=h[i+1];
    }
    db ql=1e9,qr=-1e9;
    for(int i=1;i<=n;++i) ql=min(ql,h[i]-r[i]),qr=max(qr,h[i]+r[i]);
    printf("%.2lf\n",cal(ql,qr,g(ql,qr))*2);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章