5.21 省选模拟赛 luogu P4207 [NOI2005]月下柠檬树 解析几何 自适应辛普森积分法
LINK:月下柠檬树
之前感觉这道题很鬼畜 实际上 也就想到辛普森积分后就很好做了.
辛普森积分法的式子不再赘述 网上多的是.值得一提的是 这道题利用辛普森积分法的话就是一个解析几何的问题 而并非计算几何.
求面积的并也没有什么好的方法 不能使用半平面交 因为不是一个凸多边形.
决定使用辛普森之后 容易想到 要求出 函数f(x)的式子.
考虑 当x处于一个圆中时 容易求得f(x) 利用勾股定理即可 考虑在梯形内时 容易发现在两圆的公切线上.
求出公切线的解析式就完事了.这个问题 容易使用相似三角形等等解决。
之后需要确定 真正的范围 因为 圆可能向前或者向后覆盖.
辛普森的时候 每次求某个点的f(x)时 需要和圆或者函数取max.
然后就是常规积分了. 最难的可能是在求解析式的时候 需要仔细推.
const int MAXN=1010;
int n;
db A;//光和地面的夹角.
struct wy{db x;db r;}t[MAXN];
struct Line{db k,b,L,R;}q[MAXN];
inline db len(db a,db b)
{
return sq(a*a-b*b);
}
inline void get_tan(int x,int y)
{
if(fabs(r(x)-r(y))<=EPS)
{
q[x].L=x(x);
q[x].R=x(y);
q[x].k=0;q[x].b=r(x);
return;
}
db dx=x(y)-x(x),dr=fabs(r(x)-r(y));
//dx为两圆距离 dr 为那条边的长.
db ly,ry;
if(r(x)>r(y))
{
q[x].L=x(x)+r(x)*dr/dx;//求出cos
q[x].R=x(y)+(q[x].L-x(x))*r(y)/r(x);
ly=len(r(x),q[x].L-x(x));
ry=len(r(y),q[x].R-x(y));
q[x].k=(ry-ly)/(q[x].R-q[x].L);
q[x].b=ly-q[x].L*q[x].k;
}
else
{
q[x].R=x(y)-r(y)*dr/dx;
q[x].L=x(x)-(x(y)-q[x].R)*r(x)/r(y);
ly=len(r(x),q[x].L-x(x));
ry=len(r(y),q[x].R-x(y));
q[x].k=(ry-ly)/(q[x].R-q[x].L);
q[x].b=ly-q[x].L*q[x].k;
}
}
inline db F(db x)
{
db ans=0;
rep(1,n-1,i)if(x<x(i)+r(i)&&x>x(i)-r(i))ans=ans>len(r(i),x-x(i))?ans:len(r(i),x-x(i));
rep(1,n-1,i)if(x>=q[i].L&&x<=q[i].R)ans=ans>q[i].k*x+q[i].b?ans:q[i].k*x+q[i].b;
return ans;
}
db simpson(db a,db b)
{
db c=(a+b)/2.0;
return (b-a)*(F(a)+4*F(c)+F(b))/6.0;
}
db jf(db a,db b,db ans)
{
db c=(a+b)/2.0;
db L=simpson(a,c),R=simpson(c,b);
if(fabs(L+R-ans)<EPS)return L+R;
return jf(a,c,L)+jf(c,b,R);
}
int main()
{
//freopen("1.in","r",stdin);
gt(n);gi(A);//邻=对/tanA.
A=1/tan(A);
gi(x(1));x(1)*=A;
rep(2,n+1,i)gi(x(i)),x(i)*=A,x(i)+=x(i-1);
rep(1,n,i)gi(r(i));r(++n)=0.0;
rep(1,n-1,i)get_tan(i,i+1);
db ll=x(1)-r(1),rr=x(n);
rep(1,n,i)
{
rr=rr<x(i)+r(i)?x(i)+r(i):rr;
ll=ll>x(i)-r(i)?x(i)-r(i):ll;
}
printf("%.2lf\n",2*jf(ll,rr,simpson(ll,rr)));
return 0;
}