CodeForces 1060G Balls and Pockets

发布时间 2023-11-02 18:59:46作者: zltzlt

洛谷传送门

CF 传送门

NOIP 模拟赛 T2。很厉害的题。

想象数轴上 \(a_1, a_2, \ldots, a_n\) 位置上各有一个洞,每个非负整数位置上有一个点。

每次操作相当于,对于每个点,如果它刚好位于一个洞,那么它会掉进去;否则设它的位置为 \(p\),位置在它前面的洞有 \(t\) 个,那么这个点的位置变成 \(p - t\)。容易发现每时刻每个点的位置互不相同。

一次询问 \((x, k)\) 相当于,进行 \(k\) 次操作后,找到数轴上位置为 \(x\) 的点,求它进行所有操作前的位置。

单点的移动没有很好的性质。考虑一段连续点的移动。比如极远处 \(L = 10^{18}\) 有一段连续点 \([L, L + n - 1]\);那么容易发现这些点不重不漏地经过 \([a_1, L + n - 1]\) 的位置。并且每一时刻连续点都是一段连续的区间。

发现对于一个 \([a_i, a_{i + 1})\) 段内的移动,连续点的区间 \([L, L + i - 1]\) 只会重复地进行 \(L \gets L - i\) 的变化,直到连续点覆盖了 \(a_i\)。那么我们只需要维护当连续点的区间覆盖了一个洞时连续点的变化(有哪些点掉洞里了)。

对于一个询问 \((x, k)\),设在 \(t\) 时刻连续点覆盖了 \(x\),就相当于求它 \(t - k\) 时刻的位置。

那么每个询问都可以被转化成,初始位于 \(L + x\) 的点,求它 \(k\) 时刻的位置。询问全部离线下来按 \(k\) 从小到大排序,直接模拟一遍即可。

使用树状数组维护初始 \([L, L + n - 1]\) 的连续点哪些掉洞里了。

时间复杂度 \(O((n + m) \log n)\)

code
// Problem: G. Balls and Pockets
// Contest: Codeforces - Codeforces Round 513 by Barcelona Bootcamp (rated, Div. 1 + Div. 2)
// URL: https://codeforces.com/problemset/problem/1060/G
// Memory Limit: 512 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 100100;

ll n, m, a[maxn], ans[maxn];

namespace BIT {
	ll c[maxn];
	
	inline void init() {
		mems(c, 0);
	}
	
	inline void update(ll x, ll d) {
		for (int i = x; i <= n; i += (i & (-i))) {
			c[i] += d;
		}
	}
	
	inline ll query(ll x) {
		ll res = 0;
		for (int i = x; i; i -= (i & (-i))) {
			res += c[i];
		}
		return res;
	}
	
	inline ll kth(ll k) {
		ll s = 0, p = 0;
		for (int i = (1 << __lg(n)); i; i >>= 1) {
			if (p + i <= n && s + c[p + i] < k) {
				p += i;
				s += c[p];
			}
		}
		return p + 1;
	}
}

struct node {
	ll x, k, id;
} qq[maxn];

void solve() {
	scanf("%lld%lld", &n, &m);
	mems(ans, -1);
	for (int i = 1; i <= n; ++i) {
		scanf("%lld", &a[i]);
		BIT::update(i, 1);
	}
	for (int i = 1; i <= m; ++i) {
		scanf("%lld%lld", &qq[i].x, &qq[i].k);
		qq[i].id = i;
		if (qq[i].x < a[1]) {
			ans[i] = qq[i].x;
		}
	}
	sort(qq + 1, qq + m + 1, [&](const node &a, const node &b) {
		return a.x > b.x;
	});
	ll l = 1e18, t = 0;
	for (int i = 1, j = n; i <= m && j;) {
		ll d = (l - a[j] + j - 1) / j;
		ll k = d * j;
		while (i <= m && qq[i].x >= l - k) {
			if (ans[qq[i].id] != -1) {
				++i;
				continue;
			}
			qq[i].k = t + (l - qq[i].x + j - 1) / j - qq[i].k;
			qq[i].x = BIT::kth(qq[i].x - (l - (l - qq[i].x + j - 1) / j * j) + 1);
			++i;
		}
		while (j && a[j] >= l) {
			ll t = BIT::kth(a[j] - l + 1);
			BIT::update(t, -1);
			--j;
		}
		l -= k;
		t += d;
	}
	BIT::init();
	for (int i = 1; i <= n; ++i) {
		BIT::update(i, 1);
	}
	sort(qq + 1, qq + m + 1, [&](const node &a, const node &b) {
		return a.k < b.k;
	});
	l = 1e18;
	t = 0;
	for (int i = 1, j = n; i <= m && j;) {
		ll d = (l - a[j] + j - 1) / j;
		ll k = d * j;
		while (i <= m && qq[i].k <= t + d) {
			if (ans[qq[i].id] != -1) {
				++i;
				continue;
			}
			ans[qq[i].id] = l - 1 + BIT::query(qq[i].x) - (qq[i].k - t) * j;
			++i;
		}
		while (j && a[j] >= l) {
			ll t = BIT::kth(a[j] - l + 1);
			BIT::update(t, -1);
			--j;
		}
		l -= k;
		t += d;
	}
	for (int i = 1; i <= m; ++i) {
		printf("%lld\n", ans[i]);
	}
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}