1. 程式人生 > 實用技巧 >CF1407D Discrete Centrifugal Jumps 題解

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, h_{x + 1} ... h_{i - 1})\)

考慮使用單調棧。單調棧裡面的節點滿足嚴格遞增。

那麼又因為要求 \(h_i > max(h_x, h_{x + 1} ... 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;
}