公式

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;
}

投稿日時:
最終更新: