题目:
题解:
题目就是让求
我们先理解一下这道题目:
首先我们可以给所有路段随意分配一个速度
接下来,我们需要在一些路段上 消耗更多能量来提速,以此来 缩短一定的时间。不同的路段,花费单位能量缩短的时间(简称“ 性价比”)是不同的,所以要模拟这个过程,一定是每时每刻都在 当前性价比最高的路段上花费能量,直到能量花完为止。(相对的,我们可以减少某一条路径的能量,增加某路径所需的时间,然后把能量用到别的地方去)
根据前面的式子我们可以发现:随着花费能量的增加,性价比会越来越低
这样的话,只要按照上面这种贪心策略,时时刻刻在性价比最高的路段花费能量,(并且使他的性价比降低),最后到达最优解的时候,各路的性价比会一样
这个性价比是什么呢?t-E函数的斜率——导数。那么最优解满足,各路段导数一样
那么我们二分这个导数,求出每段的速度,求出所有的能量,和总能量比较一下,就可以得出答案了
我们要求出每一段导数关于v的关系。(k=s*k)
求谁的导数啊?我们考虑变化1单位的速度,t会变化t’,E会变化E’(x’为x的导数),我们要找到t’/E’最大的那个增加速度,也就是要求:
然后二分公共导数x,对于每段路解方程: (二分解方程)得到v,进而求出所需要的能量。
竟然卡我精度= =,开到1e-12,为了防止/0,我们需要在可能出现0的getv函数里返回二分值
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const double eps=1e-12;
const int N=100005;
double s[N],k[N],v[N],E;int n;
double getv(double x,int i)
{
double l=max(v[i],(double)0),r=(double)N;
while (r-l>=eps)
{
double mid=(l+r)/2;
if (2*k[i]*mid*mid*(mid-v[i])*x>-s[i]) l=mid;else r=mid;
}
return (l+r)/2;
}
double nl(double x)
{
double sum=0;
for (int i=1;i<=n;i++)
{
double vv=getv(x,i);
sum+=k[i]*(vv-v[i])*(vv-v[i]);
}
return sum;
}
int main()
{
scanf("%d%lf",&n,&E);
for (int i=1;i<=n;i++) scanf("%lf%lf%lf",&s[i],&k[i],&v[i]),k[i]*=s[i];
double l=-N,r=0;
while (r-l>=eps)
{
double mid=(l+r)/2;
if (nl(mid)<=E) l=mid;else r=mid;
}
double ans=0;
for (int i=1;i<=n;i++) ans+=s[i]/getv(l,i);
printf("%.10lf",ans);
}