[BZOJ 2876][NOI2012]骑行川藏
我终于A了……不就是拉格朗日乘数法的模板题吗
首先这道题最优情况下一定有(这里用Ei表示第i段路程的耗能):∑ni=1Ei=Eu。
然后这个东西是一个等式限制条件,然后我们还要最小化总用时,给人拉格朗日乘数法的即视感……
不管怎么说让我们来列式子吧:
h(x1,x2,…,xn,λ)=n∑i=1sixi+λn∑i=1kisi(xi−vi)2
∂h∂xi=−six2i+2λkisi(xi−vi)
然后你会想这TM怎么解方程……
但是我们想一想,把∂h∂xi=0稍作整理,得:
12kiλ=x2i(xi−vi)
对于式子的左边,是一个关于xi的增函数(因为这道题默认了xi≥0且xi≥vi),然后不妨令1λ=u,可以发现u越大则xi越大!这样一来那么我们的限制条件就会更加难以满足。
所以我们可以二分这个u来解这些方程。
BTW,这题卡精度非常厉害……
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | /************************************************************** Problem: 2876 User: danihao123 Language: C++ Result: Accepted Time:3960 ms Memory:1524 kb ****************************************************************/ #include <cstdio> #include <cstring> #include <cstdlib> #include <cctype> #include <algorithm> #include <cmath> #include <iostream> #include <cassert> typedef double R; const R eps = 1e-12; const int maxn = 10005; int sign(R x) { if ( fabs (x) < eps) { return 0; } else { if (x < 0) { return -1; } else { return 1; } } } R s[maxn], k[maxn], V[maxn]; R rf( int i, R v, R delta) { return v * v * (v - V[i]) - delta; } R rf2( int i, R v) { return 3 * v * v - 2 * v * V[i]; } R gen_rt( int i, R delta) { R x = 1e6; int lambda = 100; while (lambda --) { R a = rf(i, x, delta); if (sign(a) == 0) break ; R da = rf2(i, x); x -= a / da; } return x; } int n; R E; R gen_lft() { R l = 1e-14, r = 1e16; while (r - l > eps) { #ifdef LOCAL printf ( "State [%.18lf, %.18lf]\n" , l, r); #endif R M = (l + r) / 2; R T = 0; for ( int i = 1; i <= n; i ++) { R v = gen_rt(i, M / (2 * k[i])); T += (v - V[i]) * (v - V[i]) * k[i] * s[i]; } if (sign(T - E) <= 0) { l = M; } else { r = M; } } return l; } int main() { std::cin >> n >> E; for ( int i = 1; i <= n; i ++) { std::cin >> s[i] >> k[i] >> V[i]; } R M = gen_lft(); R tm = 0; for ( int i = 1; i <= n; i ++) { R v = gen_rt(i, M / (2 * k[i])); tm += s[i] / v; } printf ( "%.8lf\n" , tm ); return 0; } |