题解:P17095 [ICPC 2017 Qingdao R] Enclosure of Land and Water
lailai0916 · · 题解
题意简述
起点到直线海岸的距离为
解题思路
先定义两个圆弧函数。设弦长为
弧长为
若曲线完全位于陆地,等周不等式给出一个周长为
若曲线经过水域,可先将凹陷部分向外凸化。该操作不增加路程,却不会减小面积,因此最优曲线只与海岸相交两次。
固定同一介质内一段边界的端点与长度,圆弧围出的面积最大。若两段陆地圆弧半径不同,还能在两段之间重新分配少量长度,使总面积增大。因此两段陆地圆弧半径相同,接岸角也相同。
设半径为
等式右侧关于
光滑类中,设陆地、水域圆弧的圆心角为
对应价值为:
陆地圆弧能经过起点,当且仅当
光滑类的内部极值可以直接求出。令
消去
记等式右侧为 atan2 求
高度取等号时,起点是完整陆地圆弧的最深点。将该圆弧从起点切开,恰好得到下一类中的两段对称圆弧。其余退化边界也会落入全陆圆或下一类的边界,因此不会遗漏。
对称类中,设海岸交点到起点垂足的距离为
时间条件为
陆地与水域面积分别为:
这里
为了说明内部极值的约束,令
对
第一式来自弧长分配与交点移动,第二式就是时间条件。边界则是
代码先计算
设网格边数为
参考代码
#include <bits/stdc++.h>
using namespace std;
using ld=long double;
const int M=200;
const int N=M+5;
const double pi=acos(-1);
const double neg=-1e100;
double d,v,w,vl,vw,t,lim;
double val[N][N];
double coef(double a)
{
return a<1e-4?2+a*a/12+7*a*a*a*a/2880:a/sin(a/2);
}
double segment(double c,double a)
{
if(a<1e-4)return c*c*(a/12+a*a*a/360+a*a*a*a*a/10080);
double s=sin(a/2);
return c*c*(a-sin(a))/(8*s*s);
}
double corner(double a,double b)
{
double p=coef(a)/v;
if(p*d>t+1e-12)return neg;
double al,aw;
if(2*pi-b<1e-10)
{
double r=max(0.0,t-p*d);
al=2*segment(d,a);
aw=w*w*r*r/(4*pi);
}
else
{
double q=coef(b)/w,e=max(0.0,(t-p*d)*(t+p*d)),x=e/(t*q+p*sqrt(e+d*d*q*q));
al=d*x+2*segment(hypot(d,x),a);
aw=segment(2*x,b);
}
return vl*al+vw*aw;
}
double smooth(double a,double b)
{
if(a<1e-10||2*pi-b<1e-10)return neg;
double s=sin(a/2),r=t/(a/v+s*coef(b)/w);
if(r*(1-cos(a/2))+1e-12<d)return neg;
double al=r*r*(a-sin(a))/2,aw=segment(2*r*s,b);
return vl*al+vw*aw;
}
double smooth_best()
{
ld lv=v,lw=w,lvl=vl,lvw=vw,den=lw*lw*(lvw*lvw-lvl*lvl);
if(den==0)return neg;
ld num=lvl*lvl*(lv*lv-lw*lw),z=num/den;
if(z<=0||z>1+1e-12)return neg;
z=max(0.0L,min(z,1.0L));
ld x=asinl(sqrtl(z)),ans=neg;
for(int i=0;i<2;i++)
{
ld a=i?pi-x:x;
if(i&&fabsl(a-x)<1e-15)continue;
ld sb=lvw*lw/(lvl*lv)*sinl(a),cb=-lw/lv*cosl(a);
if(fabsl(sb*sb+cb*cb-1)>1e-8)continue;
ld b=atan2l(sb,cb);
ans=max(ans,ld(smooth(2*a,2*b)));
}
return ans;
}
double opt(double la,double lb,double (*fun)(double,double))
{
double da=la/M,db=lb/M,ans=0;
for(int i=0;i<=M;i++)
{
for(int j=0;j<=M;j++)
{
val[i][j]=fun(i*da,j*db);
ans=max(ans,val[i][j]);
}
}
for(int i=0;i<=M;i++)
{
for(int j=0;j<=M;j++)
{
if(val[i][j]<neg/2)continue;
bool ok=1;
for(int k=0;k<9;k++)
{
int x=i+k/3-1,y=j+k%3-1;
if(x<0||x>M||y<0||y>M)continue;
if(val[x][y]>val[i][j]||(val[x][y]==val[i][j]&&make_pair(x,y)<make_pair(i,j)))ok=0;
}
if(!ok)continue;
double a=i*da,b=j*db,sa=da,sb=db,cur=val[i][j];
int cnt=80;
while(cnt--)
{
double na=a,nb=b,bst=cur;
for(int k=0;k<9;k++)
{
int x=k/3-1,y=k%3-1;
double aa=max(0.0,min(a+x*sa,la)),bb=max(0.0,min(b+y*sb,lb)),res=fun(aa,bb);
if(res>bst)
{
bst=res;
na=aa;
nb=bb;
}
}
if(bst>cur)
{
a=na;
b=nb;
cur=bst;
}
else
{
sa/=2;
sb/=2;
}
}
ans=max(ans,cur);
}
}
return ans;
}
double solve()
{
double ans=vl*v*v*t*t/(4*pi);
ans=max(ans,smooth_best());
if(2*d/v>t)return ans;
if(pi*d/v<=t)lim=pi;
else
{
double l=0,r=pi;
for(int i=0;i<80;i++)
{
double mid=(l+r)/2;
if(coef(mid)*d/v<=t)l=mid;
else r=mid;
}
lim=l;
}
return max(ans,opt(lim,2*pi,corner));
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin>>T;
cout<<fixed<<setprecision(3);
while(T--)
{
cin>>d>>v>>w>>vl>>vw>>t;
cout<<solve()<<'\n';
}
return 0;
}