P9290 [ROI 2018] Decryption 题解
题目大意#
给定一个长度为 的序列 ,对于 的一个子段 ,若 ,则这个子段是合法的,问如何划分才能使合法的子段数最小,输出最小子段数。
思路#
首先,我们预处理出 右侧第一个小于它的数的位置 ,和 右侧第一个大于等于它的数 ,可以用单调栈预处理出来。
对于一个子段 ,若 ,则使 ,直到不满足此条件。
这么做的道理很简单。要想使合法子段数最小,显然要使子段长度最长。对于一个左端点 ,它所对应的能使子段长度最长的右端点 一定是在区间 中的最大值的位置,如果最大值在这段区间中出现了多次,则取最靠右的位置。
Code#
#include<bits/stdc++.h>
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#define reg register
#define int long long
using namespace std;
inline int read()
{
short f=1;
int x=0;
char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9') {x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return x*f;
}
int n,a[300010],mx[300010],mn[300010],ans,nxt[300010],pre[300010];
stack<int>maxx,minn;
signed main()
{
n=read();
for(reg int i=1;i<=n;i=-~i) a[i]=read();
maxx.push(1);minn.push(1);
for(reg int i=2;i<=n;i=-~i)
{
while(!maxx.empty()&&a[i]>=/*大于等于,因为会有相同的值,赛时没写等号挂了*/a[maxx.top()]) mx[maxx.top()]=i,maxx.pop();
while(!minn.empty()&&a[i]<a[minn.top()]) mn[minn.top()]=i,minn.pop();
maxx.push(i);minn.push(i);
}
while(!maxx.empty()) mx[maxx.top()]=n+1,maxx.pop();
while(!minn.empty()) mn[minn.top()]=n+1,minn.pop();
for(reg int l=1,r=1;r<=n;r=-~r)
{
while(mx[r]<mn[l]&&r!=mx[r]&&r<=n) r=mx[r];
ans=-~ans;l=-~r;
}
return printf("%lld",ans),0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】