题面

https://www.luogu.com.cn/problem/P3572

题解

首先,不难列出方程:$f[i]$ 代表跳到 $i$ 的最小疲劳值

$$
f[i] = \min_{i - k \leq j < i} \{f[j] + (a[i] \geq a[j])\}
$$

时间复杂度为 $O(qn^2)$,不理想。但是不难发现,这道题可以用单调队列进行优化。为什么?

如下状态一定是更优的:随着 index 递增

  • $f$ 不下降(第一维比较)
  • $f$ 若相同,同时有 $a$ 不上升(第二维比较)

所以我们就可以用单调队列来维护 index,时间复杂度为 $O(qn)$

注意:

  • 初值的设定,详见代码
  • 写的顺序和单调队列的模板稍微有一点不同,因为 $i - k \leq j < i$,右边的小于号不是小于等于号,即 $f[i]$ 不能被自己更新,所以调整了一下逻辑块的顺序。

代码

#include <iostream>

using namespace std;

const int maxn = 1e6+5;

int qq, n, k, head, tail, a[maxn], f[maxn], q[maxn];

int main() {
    cin >> n;
    for (int i = 1; i <= n; i ++) cin >> a[i];
    cin >> qq;
    while (qq --> 0) {
        cin >> k;
        head = 1; tail = 1; q[1] = 1;
        for (int i = 2; i <= n; i ++) {
            while (head <= tail && q[head] < i - k) head ++;
            f[i] = f[q[head]] + (a[i] >= a[q[head]]);
            while (head <= tail && (
                f[i] < f[q[tail]] || (
                    f[i] == f[q[tail]] && a[i] >= a[q[tail]]
                )
            )) tail --;
            q[++ tail] = i;
        }
        cout << f[n] << endl;
    }
    return 0;
}
最后修改:2022 年 04 月 08 日 11 : 50 PM
真的不买杯奶茶嘛....qwq