首先我们可以将 (t_i imes v_i) 看作一个整体,不妨令 (x_i = v_i, y_i = t_i imes v_i) 这样两堆水混合后相当于将两个维度相加,方便了计算。
那么在某一时刻,混合出来的水一定是由某些水的一部分所组成的,贪心地想:让温度更高的水占得尽可能多一定是更优的。
但是会存在一个问题,每天的水都必须全部倒进去,然后才能放水,这样温度低的水可能会拖累温度高的水。
但基于观察可以发现:
-
如果当前倒进来的水比之前的水温更低,那么先将两者混合一定是更优的。
-
否则为了保证之后能装水,一定是将之前的水先排出再排当前的水。
由此可以看出,每一时刻最优情况下混合的水温度一定是按时间单调递增的。
这很类似一个单调队列,那么我们可以考虑使用单调队列来维护这个单调递增的即将混合的水的序列。
每次进来这个时刻的水,因为先排放水温低的水是更优的,所以先需要把队首的水排出,直到恰好加入进来的水能放满水库。
那么此时的情况对于当前时刻就是最优的。
下面就需要基于上面的哪个观察来调整使得之后的情况也是更优的。
如果加入的水温度比队尾要高,直接加入即可。
否则,我们就一直将队尾与当前水混合直到当前水温比队尾高位置。
可以发现,这样满足了上面的两条调整策略。
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define rep(i, l, r) for (int i = l; i <= r; ++i)
const int N = 5e5 + 5;
double Sx, Sy, y[N];
int n, v, t, l, r, L, x[N], q[N];
int read() {
char c; int x = 0, f = 1;
c = getchar();
while (c > '9' || c < '0') { if(c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
signed main() {
n = read(), L = read();
rep(i, 1, n) t = read(), v = read(), x[i] = v, y[i] = v * t;
l = 1;
rep(i, 1, n) {
for (; l <= r && Sx + x[i] > L; ) {
if(x[q[l]] <= Sx + x[i] - L) Sx -= x[q[l]], Sy -= y[q[l]], ++l;
else {
Sy -= 1.0 * (Sx - (L - x[i])) / x[q[l]] * y[q[l]];
y[q[l]] = 1.0 * (x[q[l]] - (Sx - (L - x[i]))) / x[q[l]] * y[q[l]];
x[q[l]] -= Sx - (L - x[i]);
Sx = L - x[i];
}
}
for (; l <= r && y[q[r]] / x[q[r]] > y[i] / x[i]; --r)
y[i] += y[q[r]], x[i] += x[q[r]], Sx -= x[q[r]], Sy -= y[q[r]];
q[++r] = i, Sx += x[i], Sy += y[i];
printf("%.7f
", Sy / Sx);
}
return 0;
}
可以发现,本题的精髓在于使用单调队列来模拟优化了两条贪心策略。