Description
n个人排成一条直线(一排),给出队伍中每个人的身高,每个人只能看到站在他右边且个头比他小没有被其他人挡住(跟他身高相同也会挡出他)的人。请求出所有人可以看到的人数之和。
1<=N<=80,000
Sample Input
6
10
3
7
4
12
2
Sample Output
5
分析
这题就是单调栈的模板题。
先来讲一下单调栈。
1、定义
单调栈是一种特殊的栈,其栈内的元素都保持一个单调性(单调递增或者递减)。
单调递增栈,从栈底到栈顶依次递增(单调非递减栈:允许有相等)
单调递减栈,从栈底到栈顶依次递减(单调非递增栈:允许有相等)
2、维护
维护单调栈的方式类似于维护单调队列。单调栈是单向进单向出,只要while依次判断栈顶的那个数是否符合栈的单调性,如果不符合就弹出,符合就把新的值压入栈。
用一个生活中的例子来模拟一下单调栈的维护过程
我们借用拿号排队的场景来说明下。现在有很多人在排队买可乐,每个人手里都拿着号,越靠前的人手里的号越小,但是号不一定是连续的。小明拿了号后并没有去排队,而是跑去约会了。等他回来后,发现队伍已经排得很长了,他不能直接插入到队伍里,不然人家以为他是来插队的。小明只能跑到队伍最后,挨个询问排队人手里的号,小明认为号比他大的人都是“插队”的,于是小明就会施魔法把这些人变消失,直到小明找到号比他小的为止。
在上面这个场景里,大家排的队伍就像是单调栈,因为大家手里拿的号是单调递增的。而小明找自己位置的这个过程就是元素加入单调栈的过程。新加入的元素如果加到栈顶后,如果栈里的元素不再是单调递增了,那么我们就删除加入前的栈顶元素,就像小明施魔法把“插队”的人变消失一样。直到新元素加入后,栈依然是单调递增时,我们才把元素加进栈里。
其他的详见注释。
上代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
long long n,a[100001],st[100001],top=1,ans;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
a[n+1]=0x7fffffff;
st[1]=n+1;
for(int i=n;i>=1;i--)//从右边开始
{
while(a[st[top]]<a[i]&&top>0)//升序序列,比原来的小就弹出,并且栈不为空
{
top--;//减掉就没了
}
ans+=st[top]-i-1;
top++;
st[top]=i;
/*此时栈顶的数是右边第一个大于等于a[i]的数,
那么中间的数都比a[i]小,统计进答案st[++top]=i;*/
}
cout<<ans;
return 0;
}