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(vvi)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(vvi)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;
}
posted @ 2021-07-23 17:28  lahlah  阅读(30)  评论(0编辑  收藏  举报