问题引入
- 给定一个序列,求序列中每一个位置的f(i),f(i)为第i个元素右边第一个大于ai的元素的下标;
分析
- 从一个元素出发向右看,若出现单调不增部分i~j,显然 i~j不会影响i左边的f值,整个问题的求解与单调性有关。
- 单调栈:字面意思,具有元素单调性的栈;在这个题中我们维护一个自栈顶至栈底单调递增的栈,从右向左扫一遍,若发现当前元素大于等于栈顶元素,就不断的删除栈顶,最后栈顶元素(如果有)的下标就是当前元素的f值,然后再将当前元素加入栈。单调栈的精髓在于每个元素只需入栈一次,可以实现O(1)得到答案。
#include<iostream>
#include<cstdio>
int n,a[3000005],sta[3000005],tp,ans[3000005];
using namespace std;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=n;i>=1;i--)
{
while(tp&&a[sta[tp]]<=a[i]) tp--;
ans[i]=tp?sta[tp]:0;
sta[++tp]=i;
}
cout<<ans[1];
for(int i=2;i<=n;i++)
cout<<' '<<ans[i];
return 0;
}
#include<iostream>
#include<cstdio>
#include<cmath>
int n,tp;
long long h[100010],maxn,f[100010],len,sta[100010];
using namespace std;
int main()
{
while(scanf("%d",&n)&&n)
{
tp=len=0;
for(int i=1; i<=n; i++)
{
scanf("%lld",&h[i]);
f[i]=0;
}
for(int i=1; i<=n; i++)
{
if(sta[tp]>h[i])
{
len=0;
while(tp&&sta[tp]>h[i])
{
len+=f[tp];
maxn=max(maxn,sta[tp--]*len);
}
sta[++tp]=h[i];
f[tp]=len+1;
}
else
{
sta[++tp]=h[i];
f[tp]=1;
}
}
len=0;
while(tp)
{
len+=f[tp];
maxn=max(maxn,sta[tp--]*len);
}
cout<<maxn<<endl;
maxn=0;
}
return 0;
}
#include<iostream>
#include<cstdio>
using namespace std;
long long n,a[500005],sta[500005],tp,ans=-1,num[500005];
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(int i=1;i<=n;i++)
{
if(tp&&a[i]>sta[tp])
{
while(tp&&a[i]>sta[tp])
{
tp--;
ans++;
}
if(tp&&a[i]==sta[tp])
ans+=(num[tp]+(tp>num[tp]?1:0));
else ans+=(tp?1:0);
}
else
ans+=((a[i]==sta[tp])?(num[tp]+(tp>num[tp]?1:0)):1);
sta[++tp]=a[i];
num[tp]=1;
if(a[i]==sta[tp-1]) num[tp]=num[tp-1]+1;
}
cout<<ans;
return 0;
}
- 模板题,用了两种实现方法,单调栈倒着扫,单调队列正着扫。
#include<iostream>
#include<cstdio>
using namespace std;
int n,h[100005],que[100005],tp,sta[100005],ans[100005];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&h[i]);
for(int i=1;i<=n;i++)
{
while(tp&&h[i]>h[sta[tp]])
ans[sta[tp--]]=i;
sta[++tp]=i;
}
for(int i=1;i<=n;i++)
cout<<ans[i]<<endl;
return 0;
}