公開日 :
更新日 :
単調スタックを使うと、「左(または右)で最初に出てくる、自分より大きいもの」を全体 \(O(N)\) で求められる。
自分より小さいものは、自分の後ろに隠れて二度と答えにならないので捨ててよい。
だから、スタックの中身は常に単調(奥ほど大きい)になる。
イメージ:値を「身長」と考え、人が一列に並んでいく。新しく来た人は左を向いて、自分より背の高い人の中で一番近い人を探す。
stack と vector のどちらでも書ける。速さはほぼ同じ。
| やりたいこと | stack | vector |
| 一番上に積む | st.push(x) | st.push_back(x) |
| 一番上を見る | st.top() | st.back() |
| 一番上を捨てる | st.pop() | st.pop_back() |
| 空か調べる | st.empty() | st.empty() |
| 個数 | st.size() | st.size() |
慣れるまでは stack が意味をつかみやすい。
中身を全部出力してデバッグしたいときや、スタックの途中の要素を見たいときは vector にする。
左から 1 つずつ見て、自分より小さいものを捨て、残ったトップを読み、最後に自分を積む。
#include <bits/stdc++.h>
using namespace std;
// res[i]:i より左で、a[i] より大きい最も近い要素の index(なければ -1)
vector<int> prev_greater(const vector<int>& a) {
int n = a.size();
vector<int> res(n, -1);
stack<int> st; // index を積む
for (int i = 0; i < n; i++) {
while (!st.empty() && a[st.top()] < a[i]) st.pop(); // 隠れたものを捨てる
res[i] = st.empty() ? -1 : st.top(); // トップが答え
st.push(i); // 自分を積む
}
return res;
}判断の基準は「先に確定するのはどちら側か?」の 1 つだけ。 次の 2 つを自分に聞く。
| 型 | 例 | うしろから考える理由 |
| 答えが自分より後ろで決まる | ABC372 D | 右側を見終わっていれば、必要な情報がそろっている |
| あとの操作が前の操作を上書きする | 区間を色で塗るクエリ、辺の削除クエリ | 最後の操作は上書きされないので確定。削除を逆順にすると「追加」になり、Union-Find が使える |
| ゴールの値が先に決まっている | 期待値 DP、ゲームの勝敗判定 | ゴールでは E = 0 や勝敗が確定。そこからさかのぼる |
もう 1 つのコツ:「i+1 の答えを少し直すと、i の答えになる?」と考える。
ABC372 D は「真隣を足して、それより低いものを捨てる」だけで次の答えが作れる。