Official

C - レ/V Editorial by Nyaan


この問題は、問題文に書かれている操作を適切に実装すると解くことができる問題です。
ポイントは最小の整数 \(x\) を含む連結成分を求める部分です。これは Union Find や BFS などのアルゴリズムを利用しても求められますが、ここでは「レ」の性質を利用します。すなわち、問題文のグラフ \(G\) は 1-2-3-4, 5, 6, 7-8-9-10 というように連続する整数が繋がった形をしているので、小さい方から「最小の整数と連結である間、区間を伸ばす」という方法で調べていけば連結成分を検出できます。

よって、次のような手順で計算できます。

  • 答えを管理する配列を \(\mathrm{ans}\) とする。はじめ \(\mathrm{ans}\) は空である。
  • また、\(L = 1\) とする。\(L\) は調べていない整数のうち最小の整数を意味する。
  • \(L \leq N\) である間、次の操作を繰り返す。
    • \(R = L\) とする。そして、\(R\)\(R+1\) の間に「レ」が存在する間、\(R\)\(1\) を加える。
    • \(L, L+1, \dots, R\) は連結成分なので、\(L\) 以上 \(R\) 以下の整数を大きい方から順に \(\mathrm{ans}\) に追加する。
    • \(R\) までは調べ終わったので、\(L \gets R + 1\) に更新する。
  • 上の loop を終えた時点での \(\mathrm{ans}\) の列が答えとなる。

この手順は適切に実装すれば \(\mathrm{O}(N^2)\)\(\mathrm{O}(N)\) で動作して、十分高速です。

  • 実装例(C++)
#include <iostream>
#include <vector>
using namespace std;

int main() {
  int N, M;
  cin >> N >> M;
  vector<int> a(M);
  for (auto& x : a) cin >> x;
  vector<int> re(N + 1);
  for (auto& x : a) re[x] = 1;
  vector<int> ans;
  for (int i = 1, j = 1; i <= N; i = ++j) {
    while (re[j]) j++;
    for (int k = j; k >= i; k--) ans.push_back(k);
  }
  for (int i = 0; i < N; i++) cout << ans[i] << " \n"[i + 1 == N];
}

posted:
last update: