けんちょんの競プロ精進記録

競プロの精進記録や小ネタを書いていきます

AtCoder ABC 351 C - Merge the balls (4Q, 灰色, 250 点)

スタックのいい練習問題!

問題概要

空の列と  N 個のボールがある。 i 番目のボールの大きさは  2^{A_{i}} である。これから  N 回の操作を行う。 i 回目の操作では、 i 個目のボールを列の一番右に付け加えた後、次の手順を繰り返す。

  • 列にあるボールが 1 つ以下ならば操作を終了する
  • 列にあるボールのうち右から 1 番目のものと 2 番目のものの大きさが 異なるならば操作を終了する
  • 列にあるボールのうち右から 1 番目のものと 2 番目のものの大きさが 等しい ならば、2 つのボールを取り除き、「取り除かれた 2 つのボールの大きさの和」の大きさのボール 1 つを列の一番右に付け加えるその後、1. に戻り、手順を繰り返す

N 回の操作の後で、列にあるボールの数を求めよ。

制約

  •  1 \le N \le 2 \times 10^{5}

考えたこと

列はスタックで管理しよう。C++ では具体的には vector<int> 型の変数 stk で管理するとよい。右端のボールは、stk.back() によって取得できる。

一般に、大きさが  2^{m} のボールを 2 つ合わせると、 2^{m+1} のボールになることに注意しよう。これに注意して、1 回の操作 (大きさ  2^{A} のボールを右端に入れる操作) は次のように実装できる。

while (!stk.empty() && stk.back() == A) {
    stk.pop_back();
    ++A;
}

全体として、計算量は  O(N) となる。

コード

#include <bits/stdc++.h>
using namespace std;

int main() {
    int N;
    cin >> N;
    vector<int> stk;
    for (int i = 0; i < N; ++i) {
        long long A; cin >> A;
        while (!stk.empty() && stk.back() == A) {
            stk.pop_back();
            ++A;
        }
        stk.push_back(A);
    }
    cout << stk.size() << endl;
}