CF1407D Discrete Centrifugal Jumps 题解
蒟蒻语
写了 \(100\) 行的 线段树上 ST 表维护二分维护单调栈维护 dp, 结果最后发现只要俩单调栈就好了 = =
蒟蒻解
首先 \(dp_i\) 表示从 \(1\) 楼到 \(i\) 楼要跳几次。
题目中有 3 个条件, 对三个条件分别设 \(dp\) 方程。
第一个很显然, 就是 : \(dp_i = dp_{i - 1} + 1\)
第二个怎么弄呢?
考虑看有哪些点是可能来更新这个点的。
假设 \(x\) 位置可以来更新 \(i\) 位置。那么 \(h_x > max(h_{x+1}, h_{x + 2} ... h_{i - 1})\)。
考虑使用单调栈。单调栈里面的节点满足严格递增。 这样 \(x\) 就一定是单调栈中的节点了
因为要求 \(h_i > max(h_{x+1}, h_{x + 2} ... h_{i - 1})\),所以如果单调栈中有一个数比他大,那么单调栈中在他之后的节点就不能更新他了。
可以在单调栈中边弹点边更新答案。
第三个条件和第二个几乎一样,不说了。
蒟蒻码
细节看代码吧。
#include<bits/stdc++.h>
using namespace std;
const int N = 3e5 + 7;
int n, m, s[N], dp[N], atot, a[N], btot, b[N];
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", &s[i]);
memset(dp, 0x3f, sizeof(dp));
a[++atot] = 1, b[++btot] = 1, dp[1] = 0;
for(int i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + 1;
while(atot && s[i] >= s[a[atot]]) {
if(s[i] != s[a[atot]]) dp[i] = min(dp[i], dp[a[atot - 1]] + 1);
--atot;
}
while(btot && s[i] <= s[b[btot]]) {
if(s[i] != s[b[btot]]) dp[i] = min(dp[i], dp[b[btot - 1]] + 1);
--btot;
}
a[++atot] = i, b[++btot] = i;
}
printf("%d\n", dp[n]);
return 0;
}