luogu P2179 [NOI2012] 骑行川藏
https://www.luogu.com.cn/problem/P2179
首先看能量的柿子
E
=
k
i
(
v
−
v
i
)
2
s
E=k_i(v-v_i)^2s
E=ki(v−vi)2s
为了方便先把s乘进k里面
考虑到最后分配完每一段的在增加的”性价比“
T
/
E
T/E
T/E应当都是相等的
也就是说明每一段的导数都是相等的,基于这一点我们可以先求个导
d
T
d
E
=
d
T
d
v
/
d
E
d
v
=
−
s
2
k
v
2
(
v
−
v
i
)
\large \frac{dT}{dE}=\frac{dT}{dv}/\frac{dE}{dv} \\ \\\\\\= -\frac{s}{2kv^2(v-v_i)}
dEdT=dvdT/dvdE=−2kv2(v−vi)s
二分这个东西(公共导数),然后再二分得到
v
v
v进而算出总时间
code:
#include<bits/stdc++.h>
#define N 100050
using namespace std;
double V[N], K[N], S[N], E;
int n;
double getv(double x, int i) {
double l = max((double)0, V[i]), r = 1e5;
int cnt = 70;
while(cnt --) {
double mid = (l + r) / 2;
if(2 * K[i] * x * mid * mid * (mid - V[i]) > - S[i]) l = mid;
else r = mid;
}
return (l + r) / 2;
}
double calc(double x) {
double ret = 0;
for(int i = 1; i <= n; i ++) {
double v = getv(x, i);
ret += K[i] * (v - V[i]) * (v - V[i]);
}
return ret;
}
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 = - 1e9, r = 0;
int cnt = 123;
while(cnt --) {
double mid = (l + r) / 2;
if(calc(mid) <= E) l = mid;
else r = mid;
}
double ans = 0;
for(int i = 1; i <= n; i ++) ans += S[i] / getv((l + r) / 2, i);
printf("%6lf", ans);
return 0;
}