1 条题解
-
0
自动搬运
来自洛谷,原作者为

RabbitHu
这个家伙很懒,早就退役啦搬运于
2025-08-24 21:34:49,当前版本为作者最后更新于2018-05-10 15:11:15,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
(本题解同步发布于 http://www.cnblogs.com/RabbitHu/p/9019762.html, 欢迎交流)
楼上这堆题解都太难了!作为一个数学不及格选手!我来提供一个能看的题解!
预备知识只有高中数学的【导数】。不用什么偏导数/拉格朗日乘子法之类的我看不懂的东西( •̀∀•́ )!
如果你不知道什么是导数,可以找本高中数学选修2-2来看一下!看第一章第1、2节就好啦。传送门:选修2-2
感性理解一下这道题:
一开始,我们可以给所有路段随便分配一个速度。
接下来,我们需要在一些路段上耗费一定能量用来提速,以此缩短一定时间。不同路段上,花费单位能量能缩短的时间(简称“性价比”)是不同的,所以如果我们要模拟这个过程,一定是每时每刻都在当前性价比最高的路段上花费能量,直到能量花完为止。(似乎……也可以花费负的能量,增加某路段所需时间,然后把能量用到别的地方去。)
注意到一个性质:随着花费能量增加,性价比会越来越低。
这样的话,只要按照上面这种贪心策略,时时刻刻在性价比最高的路段花费能量(并使它的性价比降低),最后达到最优解时,各路段性价比会一样。
暴力模拟似乎是写不出来的,考虑更正常的做法。
这个性价比是什么呢?如果我们对每段路画出一个函数图象,表示该路段**需要的时间与花费的能量**的函数关系,那么花费一定能量之后的“性价比”是什么呢?就是函数图像上横坐标为处切线的斜率——导数。
那么最优解就满足——各路段导数一样!
同时,这个公共导数(是负的)绝对值越小(性价比越低),所需能量越多,总时间越小。
于是二分这个导数,求出每段速度,以此求出所需能量,和手里的总能量比较一下,就可以二分得到答案了!
以上是思路。现在开始数学。
要求出每段导数关于的关系。
对于一段路来说(方便起见,把乘上作为新的,就可以少写一个字母了2333):
那么
然后二分公共导数,对于每段路解方程(可二分)得到,进而求出需要的能量。
代码:
// luogu-judger-enable-o2 #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <iostream> #define enter putchar('\n') #define space putchar(' ') using namespace std; typedef long long ll; template <class T> void read(T &x){ char c; bool op = 0; while(c = getchar(), c < '0' || c > '9') if(c == '-') op = 1; x = c - '0'; while(c = getchar(), c >= '0' && c <= '9') x = x * 10 + c - '0'; if(op == 1) x = -x; } template <class T> void write(T x){ if(x < 0) putchar('-'), x = -x; if(x >= 10) write(x / 10); putchar('0' + x % 10); } const int N = 10005, INF = 0x3f3f3f3f; int n; double E, s[N], k[N], u[N]; double getv(double x, int i){ double l = max(u[i], double(0)), r = 100005, mid; int cnt = 60; while(cnt--){ mid = (l + r) / 2; if(2 * k[i] * x * mid * mid * (mid - u[i]) > -s[i]) l = mid; else r = mid; } mid = (l + r) / 2; return (l + r) / 2; } double calc(double x){ double sum = 0; for(int i = 1; i <= n; i++){ double v = getv(x, i); sum += k[i] * (v - u[i]) * (v - u[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], &u[i]), k[i] *= s[i]; double l = -INF, r = 0, mid; int cnt = 100; while(cnt--){ mid = (l + r) / 2; if(calc(mid) <= E) l = mid; else r = mid; } mid = (l + r) / 2; double ans = 0; for(int i = 1; i <= n; i++) ans += s[i] / getv(mid, i); printf("%.10lf\n", ans); return 0; }
- 1
信息
- ID
- 1138
- 时间
- 1000~2000ms
- 内存
- 500MiB
- 难度
- 7
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者