题解:B4281 [蓝桥杯青少年组国赛 2023] 月球疏散行动
link
你能找出上图的错误吗?
首先可以记录 \(a_i\) 为 \(i\) 时刻等待登机的人数,令 \(dp_i\) 为 \(i\) 时刻发车的最少等待总时间,有一个显然的 DP 方案,是这样的:
\[dp_i = \min_{j = 0}^{i - m}{(dp_j + \sum_{k = j} ^{i} a_k \times (i - k))}
\]
其含义为上一趟的总等待时间加上上一趟发车到现在的新的等待时间。
这个式子显然是 \(O(T^3)\) 的,无法接受,考虑优化。
摘去 \(\sum\)
这个东西可以用前缀和来 \(O(1)\) 求出,但是比较麻烦。
令 \(wit_i\) 为截止 \(i\) 时刻累加的总人数,令 \(sm_i\) 为截止 \(i\) 时刻那么多人的总等待时间。
可以发现 \(wit\) 是 \(a\) 的前缀和,而 \(sm\) 是 \(wit\) 的前缀和,也就是 \(a\) 的前缀和的前缀和。
那么 \(\sum _{k = j} ^i a_k \times (i - k)\) 就等于 \(sm_i - sm_j - wit_j \times (i - j)\)。
优化完的式子如下:
\[dp_i = \min_{j = 0}^{i - m}{(dp_j + sm_i - sm_j - wit_j \times (i - j))}
\]
这样就优化到了 \(O(T^2)\),仍无法接受,继续优化。
摘去 \(\min\)
观察上式,发现满足斜率优化,所以直接往上套。
- 去 \(\min\) 拆项移项: \(dp_i - sm_i + wit_j \times i = dp_j - sm_j + wit_j \times j\)。
- 按照 \(y = kx + b\) 拆分,得到:\(x = wit_j,\ k = i,\ y = dp_j + wit_j \times j - sm_j\),\(b\) 不用管。
然后显然可做,需要注意的是使用单调队列维护的时候记得插入队列的是 \(i - m\) 而不是 \(i\)。
这样就优化到了 \(O(T)\)。
代码:
// code by 樓影沫瞬_Hz17
#include <bits/stdc++.h>
using namespace std;
/*
in,out(快读快写)等缺省源内容。
*/
constexpr int N = 5e6 + 10;
int n, m, T;
int wit[N];
int sm[N];
int dp[N];
int q[N], l = 1, r = 1;
inline int X(int j) { return wit[j];}
inline int Y(int j) { return dp[j] + wit[j] * j - sm[j];}
inline int K(int i) { return i;}
signed main() {
#ifndef ONLINE_JUDGE
freopen("i", "r", stdin);
freopen("o", "w", stdout);
#endif
in(n, m);
for(int i = 1, t; i <= n; i ++)
wit[in(t) + 1] ++, T = max(T, t + 1); // t 全都加 1 是为了规避 0
for(int i = 1; i <= T + m + m; i ++) wit[i] += wit[i - 1]; // 前缀和
for(int i = 1; i <= T + m + m; i ++) sm[i] = sm[i - 1] + wit[i - 1]; // 前前缀缀和和
// 加两个 m 相当于拉空车,自动帮我们统计了答案
for(int i = 1; i <= T + m + m; i ++) {
int p = i - m;
if(p > 0) {// 插入注意是 i - m
while(l < r and (Y(q[r]) - Y(q[r - 1])) * (X(p) - X(q[r])) >= (Y(p) - Y(q[r])) * (X(q[r]) - X(q[r - 1]))) r --;
q[++ r] = p;
}
while(l < r and Y(q[l + 1]) - Y(q[l]) <= K(i) * (X(q[l + 1]) - X(q[l]))) l ++;
dp[i] = dp[q[l]] - (wit[q[l]] * (i - q[l]) + sm[q[l]]) + sm[i];
}
out(dp[T + m + m]);
}
// 星間~ 干渉~ 融解~ 輪迴~ 邂逅~ 再生~ ララバイ~```