CodeForces 548D - Mike and Feet(单调栈)
题目链接 https://vjudge.net/problem/CodeForces-548D
【题意】
给定一组长度为n的序列a[1],a[2]…a[n](n<=2e5,a[i]<=1e9),现在对于任意一个区间长度len(1<=len<=n),要求长度为len的连续子序列中最小值的最大值是多少,比如一个序列n=5,序列元素为2,3,6,4,1,那么当长度为2时答案就是4,即子序列{a[3],a[4]}中的最小值
【思路】
单调栈的基础应用,单调栈可以在O(n)的时间内处理出序列中的每一个元素的左边第一个比他大/小的元素及其位置,和第一个右边比他大/小的元素及其位置,当然这道题我们只要处理每个元素的左右两边第一处比它小的位置就行了。比如我们找到了a[i]左边第一处比他小的元素是a[x],右边第一处比他小的位置是a[y],那就说明(x,y)这个开区间里面,a[i]是最小值,说明a[i]的值就是长度=y-x-1(开区间的长度)对应的答案,对每个元素我们都这样计算一次即可得出结果。当然有些长度我们可能计算不到,比如下面的例子
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
a[i] | -inf | 1 | 2 | 3 | 4 | 5 | 5 | 3 | 2 | 1 | 6 | -inf |
le[i] | -1 | 0 | 1 | 2 | 3 | 4 | 4 | 2 | 1 | 0 | 9 | -1 |
ri[i] | -1 | 11 | 9 | 8 | 7 | 7 | 7 | 8 | 9 | 11 | 11 | -1 |
le,ri分别是左右两边第一处小于当前元素的下标位置(-1表示没有),在序列的起始位置和结束位置加入两个-inf表示负无穷大便于计算,在利用单调栈得到上面的结果后,循环计算每一个a[i],将长度为(ri[i]-le[i]-1)的答案更新成最大的a[i]即可,然后就能得到这样的结果
len | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
ans | 6 | 5 | 4 | -1 | 3 | -1 | 2 | -1 | -1 | 1 |
-1表示没有被计算过的长度,那这些长度对应的答案是多少呢?其实是它们右边的第一个不是-1的数值,即ans[4]=3,ans[6]=2,ans[8]=ans[9]=1,为什么呢?想想le,ri数组的具体含义就明白了,我们算出的(le[i],ri[i])其实是一个最长的区间,这个区间里的最小值是a[i],当有比这个区间更长的区间长度没有被计算过的话,那它的答案最大也只能和当前的答案一样了,不会再更大,因为如果可以更大的话,一定会在之前就被计算出来的。
#include<bits/stdc++.h>
using namespace std;
const int inf=2e9;
const int maxn=2e5+50;
int n;
int a[maxn];
stack<int> st;
int le[maxn],ri[maxn];
int ans[maxn];
void calc(){
while(st.size()) st.pop();
for(int i=0;i<=n+1;++i){
while(st.size() && a[i]<=a[st.top()]) st.pop();
if(st.empty()) le[i]=-1;
else le[i]=st.top();
st.push(i);
}
while(st.size()) st.pop();
for(int i=n+1;i>=0;--i){
while(st.size() && a[i]<=a[st.top()]) st.pop();
if(st.empty()) ri[i]=-1;
else ri[i]=st.top();
st.push(i);
}
}
int main(){
scanf("%d",&n);
a[0]=a[n+1]=-inf;
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
calc();
/*cout<<"下标";
for(int i=0;i<=n+1;++i) cout<<setw(3)<<i;
cout<<endl;
cout<<"a[i]";
for(int i=0;i<=n+1;++i) {
if(a[i]==-inf) cout<<" N";
else cout<<setw(3)<<a[i];
}
cout<<endl;
cout<<"le: ";
for(int i=0;i<=n+1;++i) cout<<setw(3)<<le[i];
cout<<endl<<"ri: ";
for(int i=0;i<=n+1;++i) cout<<setw(3)<<ri[i];
cout<<endl;*/
memset(ans,-1,sizeof(ans));
for(int i=1;i<=n;++i){
int len=ri[i]-le[i]-1;
ans[len]=max(ans[len],a[i]);
}
for(int i=n;i>=0;--i) ans[i]=max(ans[i],ans[i+1]);
for(int i=1;i<=n;++i){
printf("%d%c",ans[i],i==n?'\n':' ');
}
return 0;
}
/*
10
10 20 30 40 50 50 30 20 10 60
*/