Soratosorato

P9290 [ROI 2018] Decryption 题解

Sorato·2023-10-13 15:58·37 次阅读

P9290 [ROI 2018] Decryption 题解

题目大意#

给定一个长度为 n 的序列 a,对于 a 的一个子段 [al,al+1,...,ar],若 al=mini=lr(ai),ar=maxi=lr(ai),则这个子段是合法的,问如何划分才能使合法的子段数最小,输出最小子段数。

思路#

首先,我们预处理出 ai 右侧第一个小于它的数的位置 mni,和 ai 右侧第一个大于等于它的数 mxi,可以用单调栈预处理出来。

对于一个子段 [l,r],若 mxr<mnl,则使 r=mxr,直到不满足此条件。

这么做的道理很简单。要想使合法子段数最小,显然要使子段长度最长。对于一个左端点 l,它所对应的能使子段长度最长的右端点 l 一定是在区间 [l,mnl) 中的最大值的位置,如果最大值在这段区间中出现了多次,则取最靠右的位置。

Code#

Copy
#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; }
posted @   Sorato  阅读(37)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示
目录