题解:P5788 【模板】单调栈
注意:因为一些原因被打回了,改完却不能交了,但还是放这里吧。
https://www.cnblogs.com/zhangyimin12345/p/19044473/Solution-Private-P5788
单调栈,顾名思义,就是单调的栈(废话)。
即:栈内的元素具有单调性。因此可以分为单调递增栈和单调递减栈(又一句废话)。
我们先看看样例,拿这组数据手玩一遍单调递减栈。了解了解单调栈的维护过程。
\(i=1\),\(a_1=1\),此时栈内空空如也,直接压入 \(a_1\)。

\(i=2\),\(a_2=4\),\(a_2\) 大于栈顶 \(a_1=1\),为了保证这个栈的单调递减性,需要将栈顶弹出。

弹完后栈空了,压入 \(a_2\)。

\(i=3\),\(a_3=2\),\(a_3\) 小于栈顶 \(a_2=4\),可以直接压入。

\(i=4\),\(a_4=3\),\(a_4\) 大于栈顶 \(a_3=2\),需要先弹出栈顶。

此时 \(a_4\) 小于栈顶 \(a_2\),压入 \(a_4\)。

\(i=5\),\(a_5=5\),\(a_5\) 大于栈顶 \(a_4\),弹出栈顶。

但此时 \(a_5\) 仍然大于当前栈顶 \(a_2\),再次弹出栈顶。

此时栈内空了,压入 \(a_5\)。

手玩样例后,肯定能写出维护递减单调栈的代码了。
int n , a[3000010];
stack <int> st;
cin >> n;
for(int i = 1 ; i <= n ; i ++)
{
cin >> a[i];
while(!st.empty() && a[i] > a[st.top()]) st.pop();
st.push(i);
}
但是,我们连题都没看,纯属维护一个单调递减栈,所以现在好好看看题。
暴力显然趋近于 \(O(\frac{n^2}{2})\),包超时的。只能想它的标题——单调栈。
题目要求的输出是下标,显然把下标扔进栈更好处理。
再仔细想想,使栈内一个元素弹出的元素,必定是它后面的元素,而使它弹出的元素,又必定是它后面的第一个大于它的元素。
所以准备一个数组记录答案(暂叫ans),和一个栈(暂叫st),维护一个单调递减栈,每当要弹出一个元素时,令弹出元素的答案为当前令它弹出的元素的下标。
奉上代码:
# include <bits/stdc++.h>
using namespace std;
int n , a[3000010] , ans[3000010];
stack <int> st;
int main()
{
cin >> n;
for(int i = 1 ; i <= n ; i ++)
{
cin >> a[i];
while(!st.empty() && a[i] > a[st.top()]) ans[st.top()] = i , st.pop();
st.push(i);
}
for(int i = 1 ; i <= n ; i ++) cout << ans[i] << " ";
return 0;
}
也可以手写栈:
# include <bits/stdc++.h>
using namespace std;
int n , tail , a[3000010] , st[3000010] , ans[3000010];
int main()
{
cin >> n;
for(int i = 1 ; i <= n ; i ++)
{
cin >> a[i];
while(tail && a[i] > a[st[tail]]) ans[st[tail]] = i , tail --;
st[++ tail] = i;
}
for(int i = 1 ; i <= n ; i ++) cout << ans[i] << " ";
return 0;
}

浙公网安备 33010602011771号