公式

C - Except and Min 解説 by Nyaan


各クエリでは高々 \(K\)\((K \leq 5)\) のボールを取り出して、残ったボールの最小値を答えます。
ここで、全てのボールを書かれた値 \(A_i\) の昇順に並べた列を考えます。そして、先頭 \(6\) 個のボールに注目します。すると、ボールを取り出した時に \(6\) 個の中には必ず取り出されていないボールが \(1\) 個残ります。なぜならば、先頭 \(6\) 個を取り出すためには最低 \(6\) 個のボールを取り出す必要がありますが、一度に取り出すのは最大 \(5\) 個までだからです。

よって、次の方針でこの問題を解くことが出来ます。

  1. 全てのボールを書かれた値 \(A_i\) の昇順に並べた列を考えた時に、先頭 \(6\) 個に来るボールを計算する。
  2. 各クエリでは先頭から順に「そのボールが取り出されたか?」を調べる。取り出されていない最初のボールが答えとなる。

1.について、先頭 \(6\) 個を取得する方法はいくつかありますが、\((A_i, i)\) の組をソートする方法が最も簡明です。先頭 \(6\) 個を挿入ソートの要領で管理する方法もあり、この方法だと計算量が \(\mathrm{O}(N)\) になる (ソートだと \(\mathrm{O}(N \log N)\) かかってしまう) ので計算量の面ではこちらに分があります。

いずれの方法でも計算量は全体で \(\mathrm{O}(N + Q)\)\(\mathrm{O}(N \log N + Q)\) で解くことができて、十分高速です。

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

int main() {
  cin.tie(0)->sync_with_stdio(0);
  int N, Q;
  cin >> N >> Q;
  vector<pair<int, int>> A(N);
  for (int i = 0; i < N; i++) {
    cin >> A[i].first;
    A[i].second = i;
  }
  sort(begin(A), end(A));

  while (Q--) {
    int K;
    cin >> K;
    vector<int> B(K);
    for (auto& b : B) cin >> b;
    for (int i = 0;; i++) {
      if (find(begin(B), end(B), A[i].second + 1) != end(B)) continue;
      cout << A[i].first << "\n";
      break;
    }
  }
}

投稿日時:
最終更新: