题意:在满足 ∀i,vi≥0且∑ni=1kisi(vi−ti)2≤E 前提下最小化 ∑ni=1si/vi 。
学习了一下拉格朗日橙子乘子法。
对于函数
F(x1,x2,⋯,xn)
和
G(x1,x2,⋯,xn)
,在满足
G=c(c为常数)
的前提下求
F
的最小值。我们直观地想象,所有极值点一定满足
这其中的
λ
就是Lagrange Multiplier里的multiplier啦。
回到这个题。显然每段路能跑得越快越好,最后一定可以把体力给浪完,所以不等号可以换成等号。
这样就变成了
G(v1...)=E
,最小化
F(v1...)=∑ni=1si/vi
。
考虑应用拉格朗日乘子法,有
回想题目里的性质,首先 vi 一定非负,其次 vi 一定不小于 ti 。这些方程的左边是在二四象限的双曲线,右边是一条直线,因为 vi≥0 所以一定只有一个交点且 λ<0 。因此我们可以知道对于所有 λ<0 每个方程都有唯一解。现在我们只需解出满足 G=E 的 λ 。同样从图像出发,当 λ 增大时, vi 也会增大,使得 G 一定增大,因此
至此,我们只需要二分 λ 的值,然后解出每个 vi 的值,判断 G 与
时间复杂度
#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for (int i = a, _ = b; i <= _; i ++)
const int N = 10007;
const double eps = 1e-14;
#define fuck(a,b,x) a*sqr(x)*(x-b)+1
inline double sqr(double x) { return x * x; }
double s[N], k[N], t[N], E;
int n;
double solve(double a, double b) {
double l = 0, r = 1e6;
while (r - l > eps) {
double m1 = l + (r - l) / 3;
double m2 = r - (r - l) / 3;
double fl = fuck(a, b, l);
double fr = fuck(a, b, r);
double f1 = fuck(a, b, m1);
double f2 = fuck(a, b, m2);
if (fl * f1 < 0) r = m1;
else if (fl * f2 < 0) r = m2;
else if (f1 * fr < 0) l = m1;
else if (f2 * fr < 0) l = m2;
else assert(0);
}
return l;
}
double get(double l) {
double ret = 0;
rep (i , 1 , n) {
double x = solve(2 * k[i] * l, t[i]);
ret += k[i] * sqr(x - t[i]) * s[i];
}
return ret;
}
int main() {
cin >> n >> E;
rep (i , 1 , n) cin >> s[i] >> k[i] >> t[i];
double l = -1e10, r = 0;
while (r - l > eps) {
double m = l + (r - l) / 2;
if (get(m) >= E)
r = m;
else
l = m;
}
double ans = 0;
rep (i , 1 , n) {
double x = solve(2 * k[i] * l, t[i]);
ans += s[i] / x;
}
printf("%.6lf\n", ans);
return 0;
}