Description
给你 $n$ 棵树,第 $i$ 棵树的高度为 $d_i$。有一只鸟从 1 号树出发,每次飞跃不能超过 $k$ 的距离。若飞到下一棵树的高度大于等于这一棵树那么耗费一点体力,否则不会。问你飞到第 $n$ 棵树耗费最少体力为多少。多测,测试组数 $q$。
$1\leq n\leq 10^6,1\leq q\leq 25$
Solution
朴素的 $O(n^2)$ DP 是很容易想到的,记 $f_i$ 表示飞到 $i$ 上的最小花费。那么,$f_i=\min\limits_{i-j\leq k} f_j+[d_i\geq d_j]$。
容易发现,最优的答案一定可以从所有满足条件的 $j$ 中 $f_j$ 最小的地方转移过来,即 $f_i$ 的值只可能是 $\min\limits_{i-j\leq k} f_j$ 或 $\min\limits_{i-j\leq k} f_j+1$。
那么可以开一个单调队列维护满足条件的 $j$,并且满足 $f$ 单调;在 $f$ 相同的同时 $d$ 单调递减(从越高的树转移越优)。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6+5;
int n, t, a[N], k, q[N], head, tail, f[N];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
scanf("%d", &t);
while (t--) {
scanf("%d", &k);
q[head = tail = 1] = 1;
for (int i = 2; i <= n; i++) {
while (i-q[head] > k) ++head;
f[i] = f[q[head]]+(a[q[head]] <= a[i]);
while (head <= tail && (f[i] < f[q[tail]] || (f[i] == f[q[tail]] && a[i] >= a[q[tail]]))) --tail;
q[++tail] = i;
}
printf("%d\n", f[n]);
}
return 0;
}