制約的に でも通るが、スタックを用いて
でも解ける!
- 問題へのリンク(仮)
問題概要
マスからなるマス目があり、最初各マスには 0 が書かれている。このマス目に対して、以下の操作を行う。
- 正の整数値
と、連続するいくつかのマスを選ぶ(ただし、どのマスに書かれた数値も
以下でなければならない)
- これらのマスに対して、その値が
より小さいならば、
に書き換える
操作を繰り返すことにより、各マスの数値が となるようにしたい。これを実現するための操作回数の最小値を求めよ。
制約
考えたこと
基本的には、 の中に含まれる整数値のうち、小さい整数値から順に書き込んでいけばよいと考えた。その際に、すでに「より小さい整数値」が書き込まれているマスは飛ばさないといけないことに注意する。一方、「自分以上の整数値」が書き込まれているマスは一緒に塗ってしまうとしてよい。
たとえば、 であれば、次のように 4 回の操作でできる。
- 操作「10 の部分」
- マス目全体に操作して、
とする
- マス目全体に操作して、
- 操作「20 の部分」
- 10 の左側に操作して、
とする
- 10 の右側に操作して、
とする
- 10 の左側に操作して、
- 操作「30 の部分」
- 5 番目の要素に操作して、
とする
- 5 番目の要素に操作して、
この方法が最小回数になることは次のように示せる。
に含まれる各整数値について、その整数値よりも小さいマス目でマス目全体を分割したときの、その整数値を含む連結成分の個数を考える。このとき、操作回数を、その個数の総和よりも小さくはできない
- 上記の方法は、この回数を実現できる。
高速化
上記の方法を愚直に実装すると の計算量となる。それでも十分通るが、スタックを用いると
の計算量の解法に高速化できる。
具体的には、 について、次のようにすればよい。回数を表す変数
res を用意して、0 で初期化しておく。
- スタックから、
よりも大きいものは pop していく
- その後、
- スタックの末尾の要素が
に一致する場合は何もしない
- それ以外の場合は、スタックに
を push して、
resを 1 増やす
- スタックの末尾の要素が
コード
#include <bits/stdc++.h> using namespace std; int main() { int N; cin >> N; vector<int> d(N); for (int i = 0; i < N; i++) cin >> d[i]; int res = 0; vector<int> st; for (int i = 0; i < N; i++) { while (!st.empty() && st.back() > d[i]) st.pop_back(); if (!st.empty() && st.back() == d[i]) continue; else { res++; st.push_back(d[i]); } } cout << res << endl; }