题意
要求把一个序列划分成很多段,要求对于每段,最大值是末项,最小值是首项。
求最小划分段数。
解法
我们贪心来思考,若我们要保证一直到 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;
}