実行時間制限: 3 sec / メモリ制限: 1024 MB / Difficulty: 1136
問題概要
数直線上に 個の点 があり、座標は である。各 について、以下の問いの答えを出力せよ。 - 問. 点 のうち点 との距離が 番目に近い点を としたとき、点 と点 との距離を求めよ。
制約
- 入力はすべて整数。
考察
以前、 ABC365-C の記事で「ある条件を満たす の最大化・最小化」には二分探索が有効と書いたが、実は「ソートされた数列の中で 番目に条件を満たすものを求める」場合にも二分探索を使うことができる (なぜなら、ある値 を決め打ったときに、「数列の 番目の要素は 以下である」ことと「数列内に 以下の値が 個以上含まれる」ことは同値だから)。
本問では、 となる の個数を とすれば、「 を満たす の最小化」をすればよいことになる。サンプルケース1で考えてみると、下図のようになる。
距離は負になることはないので、ng
の初期値は とした。
これより、各 に関する問題は対数時間で処理することができる。
あとは をどのように求めるかだが、 は最大で [tex: 105] になるので、単純にループを回していたのでは間に合わない。そこで、ここでも二分探索を使おう。ソートした数列 に対して、閉区間 ] に含まれる要素の個数は、upper_bound(A.begin(), A.end(), b) - lower_bound(A.begin(), A.end(), a)
のように書くと求めることができる。
したがって、これも対数時間で求めることができ、実行時間に間に合う。
コード
#include <bits/stdc++.h> using namespace std; #define all(x) (x).begin(), (x).end() #define rep(i, start, end) for (auto i = (start); (i) < (end); (i)++) constexpr auto INF = 1e+9; // ======================================== // int main() { int N, Q; cin >> N >> Q; vector<int> A(N); rep(i, 0, N) cin >> A[i]; sort(all(A)); rep(q, 0, Q) { int b, k; cin >> b >> k; int ok = INF, ng = -1; while (abs(ok - ng) > 1) { int mid = (ok + ng) / 2; int right = upper_bound(all(A), b + mid) - A.begin(); int left = lower_bound(all(A), b - mid) - A.begin(); if (right - left >= k) ok = mid; else ng = mid; } cout << ok << endl; } }
実装時間: 40分
まさに二分探索のオンパレードであった。