单调栈优化DP [ROI 2018] Decryption

题意

要求把一个序列划分成很多段,要求对于每段,最大值是末项,最小值是首项。

求最小划分段数。

解法

我们贪心来思考,若我们要保证一直到 i 是合法的,左端点显然是越往左越好,但是在全局上是并没有这个性质的,所以考虑 dp;

用两个单调栈,严格单调减的 stk1, 严格单调增的 stk2。

设 dp[i] 表示划分完 1~i 的最小段数。

我们在严格单调减的栈顶往下一个就是我们最极限的往左的位置(先不考虑首项最小的性质)

之后在 stk2 upper_bound 一下这个位置,找到最极限往左的地方。

n log n 太美丽辣。

代码↓

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int MN=1e6+116;
int stk1[MN], stk2[MN], dp[MN];
int a[MN], n, top1, top2;
int main(){
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	cin>>n; for(int i=1; i<=n; ++i) cin>>a[i];
	for(int i=1; i<=n; ++i){
		while(top1&&a[stk1[top1]]<=a[i]) --top1;
		stk1[++top1]=i;
		while(top2&&a[stk2[top2]]>a[i]) --top2;
		stk2[++top2]=i;
		dp[i]=dp[*upper_bound(stk2+1,stk2+top2+1,stk1[top1-1])-1]+1;
	}
	cout<<dp[n]<<'\n';
	return 0;
}
posted @ 2025-09-29 07:57  BaiBaiShaFeng  阅读(6)  评论(0)    收藏  举报
Sakana Widget右下角定位