D - Forbidden List 2 解説
by
sheyasutaka
考察
\(A\) の順番は答えに影響しないので,\(A\) を昇順にソートして \(A_1 < A_2 < \dots < A_N\) が成り立つことを以降では仮定します.また,便宜上番兵として \(A_0 = -\infty, A_{N+1} = +\infty\) とおきます.
\(A\) にある値のうち \(X\) 以上であるような最小のものが何かは,二分探索によって求めることができます.この値を \(A_s\) とおきます.
ある整数が \(X\) 以上かつ \(A\) に含まれないことを,以下では単にその整数が条件を満たすと呼ぶことにします.
\(s \leq t \leq N+1\) において,\(X\) 以上 \(A_t\) 以下の整数は \(A_t - x + 1\) 個あり,そのうち \(A\) に含まれるものは \(t - s + 1\) 個あります.したがって,\(X\) 以上 \(A_t\) 以下の整数で \(A\) に含まれないものは \((A_t - x + 1) - (t - s + 1)\) 個です.この値は \(t\) に対して広義単調増加するので,以下の \(2\) 条件を満たす \(t_{r}\) を二分探索で求めることが可能です.
- \((A_{t_r - 1} - x + 1) - ((t_r - 1) - s + 1) < Y\)
- \((A_{t_r - 0} - x + 1) - ((t_r - 0) - s + 1) \geq Y\)
これを満たす \(t_r\) において,\(A_{t_{r} - 1}\) 以下で条件を満たす整数が \(Y\) 個未満,\(A_{t_r - 0}\) 以下で条件を満たす整数が \(Y\) 個以上あるので,答えは \(A_{t_{r} - 1}\) より大きく \(A_{t_r - 0}\) より小さいです.このとき答えを \(x_{ans}\) とおくと,\(X\) 以上 \(x_{ans}\) 以下の整数のうち \(A\) に含まれるものは \(t_r - s\) 個なので,答えの値は \(x_{ans} = X + (Y-1) + (t_r - s)\) とわかります.
実装例 (C++)
#include <iostream>
using std::cin;
using std::cout;
#include <vector>
using std::vector;
#include <algorithm>
using std::sort;
using std::lower_bound;
typedef long long int ll;
ll n, q;
vector<ll> a;
void solve () {
sort(a.begin(), a.end());
for (ll qi = 0; qi < q; qi++) {
ll x, y;
cin >> x >> y;
// a[0, si) < x && a[si, n) >= x
ll si = lower_bound(a.begin(), a.end(), x) - a.begin();
// youngest ti where |{x, x+1, ..., a[ti]} - {a[si], a[si+1], ..., a[ti]}| >= y
ll ti;
{
ll ok = n, ng = si-1;
while (ng + 1 < ok) {
ll med = (ok + ng) / 2;
if ((a[med] - x + 1) - (med - si + 1) >= y) {
ok = med;
} else {
ng = med;
}
}
ti = ok;
}
ll ans = x + (y-1) + (ti - si + 0);
cout << ans << "\n";
}
}
int main (void) {
std::cin.tie(nullptr);
std::ios_base::sync_with_stdio(false);
cin >> n >> q;
a.resize(n);
for (ll i = 0; i < n; i++) {
cin >> a[i];
}
solve();
return 0;
}
投稿日時:
最終更新:
