色んな方法があるが、できるだけ楽にやりたい!
問題概要
単調増加な 要素からなる数列
がある。この数列について、次の
回のクエリに答えよ。
【クエリ】
整数 が与えられるので、
のどれとも異なる数値 (よい数と呼ぶこととする) のうち、小さい方から
番目の値を求めよ。
制約
考えたこと
いろんな方法が考えられる。
自分が最初にやったのは「 以下のよい数は何個あるか」という判定問題を解くことで、二分探索するという方法だった。この判定問題を解くこと自体に、通常
lower_bound()
を活用する方法が考えられるので、全体として の計算量となる。
ここでは、もっと計算量の小さい方法を考える。
どの区間に属するかを求める
この問題では、実は「 番目のよい数」が
より小さい
より大きくて
より小さい
より大きくて
より小さい
- ...
より大きい
という 種類の区間のうちのどこに属するのかが、比較的簡単に分かるのだ。具体的には、次の配列を定義しよう。
←
より小さい「よい数」の個数
たとえば、 のとき、
と求められる。この配列 num
において、 がどこに属するかを考えればよい。たとえば、上の例で
ならば、その値は配列
において
と
の間にあるので、「
番目のよい数」は
と
の間にあることがわかるのだ。
より正確には、次のようにすればよい。
が
以上
未満であるとする
- 「
未満」と「
以上」の場合はよしなにする
- これは
lower_bound()
を用いて求められる
- 「
- このとき、答えは次のようになる。
が
未満の場合は、
- そうでない場合は、
この方法を用いれば、全体の計算量は となる。
コード
全体的に 0-indexed で実装した。
#include <bits/stdc++.h> using namespace std; int main() { int N, Q; cin >> N >> Q; vector<long long> A(N), C(N); for (int i = 0; i < N; ++i) { cin >> A[i]; C[i] = A[i] - i - 1; } while (Q--) { long long K; cin >> K; int i = lower_bound(C.begin(), C.end(), K) - C.begin() - 1; if (i < 0) cout << K << endl; else cout << A[i] + (K - C[i]) << endl; } }