CodeForces 1832D2 Red-Blue Operations (Hard Version)

洛谷传送门

CF 传送门

首先,如果一个点变成蓝色,在下一次立刻把它变成红色最优。这样对这个点造成的影响是减 \(1\)。直观感受一下,这样能最小化损失。并且在最后几次操作,可以把大部分甚至所有点变成蓝色。具体地,特判 \(m \le n\) 的情况后,如果 \(m\)\(n\) 奇偶性相同,最后 \(n\) 次操作会把所有点变成蓝色,否则最后 \(n - 1\) 次操作会把其中 \(n - 1\) 个点变成蓝色。

考虑二分。设二分最小值为 \(x\)。首先可以确定最后 \(n\)\(n - 1\) 次操作的对象,一定是让最小值加 \(m\),次小值加 \(m - 1\),以此类推。所以可以先对原数组排序,然后根据奇偶性分类讨论。

  • 如果 \(m,n\) 奇偶性相同,如果 \(\min\limits_{i=1}^n a_i + m - i + 1 - x < 0\) 就寄了,因为就算对这个值不进行减 \(1\),它都无法达到 \(x\);然后如果 \(\sum\limits_{i=1}^n a_i + m - i + 1 - x < \frac{m - n}{2}\) 也寄了,因为需要 \(\frac{m - n}{2}\) 次对其中某个元素减 \(1\),如果满足这个条件就不够减;否则可行。

  • 如果 \(m,n\) 奇偶性不同,最后会剩一个值加不了。这个值取 \(a_n\) 显然最优。在接下来的判断需要对 \(a_n\) 特殊处理。如果 \(a_n < x\) 或者 \(\min\limits_{i=1}^{n-1} a_i + m - i + 1 - x < 0\) 或者 \(a_n - x + \sum\limits_{i=1}^{n-1} a_i + m - i + 1 - x < \frac{m - (n - 1)}{2}\) 就寄了,否则可行。

直接暴力判断可以通过 D1。考虑优化。发现要判断的值只与 \(\sum\limits_{i=1}^n a_i - i\)\(\min\limits_{i=1}^n a_i - i\) 有关,可以做到 \(O(1)\) 判断。但是我急急忙忙敲了个整体二分才发现(

下面是整体二分的实现。

code
// Problem: D2. Red-Blue Operations (Hard Version)
// Contest: Codeforces - Educational Codeforces Round 148 (Rated for Div. 2)
// URL: https://codeforces.com/contest/1832/problem/D2
// Memory Limit: 256 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 mems(a, x) memset((a), (x), sizeof(a))

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

const int maxn = 200100;

ll n, q, a[maxn], b[maxn], m, c[maxn], ans[maxn], d[maxn];

struct node {
	ll m, id;
	node(ll a = 0, ll b = 0) : m(a), id(b) {}
} qq[maxn], qql[maxn], qqr[maxn];

void dfs1(ll l, ll r, ll ql, ll qr) {
	if (l > r || ql > qr) {
		return;
	}
	if (l == r) {
		for (int i = ql; i <= qr; ++i) {
			ans[qq[i].id] = l;
		}
		return;
	}
	ll mid = ((l + r) >> 1) + 1, t1 = 0, t2 = 0;
	for (int i = ql; i <= qr; ++i) {
		ll m = qq[i].m;
		if (mid > a[n] || c[n - 1] + m + 1 < mid || a[n] - mid + d[n - 1] + (m + 1 - mid) * (n - 1) < (m - n + 1) / 2) {
			qql[++t1] = qq[i];
		} else {
			qqr[++t2] = qq[i];
		}
	}
	int p = ql;
	for (int i = 1; i <= t1; ++i) {
		qq[p++] = qql[i];
	}
	for (int i = 1; i <= t2; ++i) {
		qq[p++] = qqr[i];
	}
	dfs1(l, mid - 1, ql, ql + t1 - 1);
	dfs1(mid, r, ql + t1, qr);
}

void dfs2(ll l, ll r, ll ql, ll qr) {
	if (l > r || ql > qr) {
		return;
	}
	if (l == r) {
		for (int i = ql; i <= qr; ++i) {
			ans[qq[i].id] = l;
		}
		return;
	}
	ll mid = ((l + r) >> 1) + 1, t1 = 0, t2 = 0;
	for (int i = ql; i <= qr; ++i) {
		ll m = qq[i].m;
		if (c[n] + m + 1 < mid || d[n] + (m + 1 - mid) * n < (m - n) / 2) {
			qql[++t1] = qq[i];
		} else {
			qqr[++t2] = qq[i];
		}
	}
	int p = ql;
	for (int i = 1; i <= t1; ++i) {
		qq[p++] = qql[i];
	}
	for (int i = 1; i <= t2; ++i) {
		qq[p++] = qqr[i];
	}
	dfs2(l, mid - 1, ql, ql + t1 - 1);
	dfs2(mid, r, ql + t1, qr);
}

void solve() {
	scanf("%lld%lld", &n, &q);
	c[0] = 1e18;
	for (int i = 1; i <= n; ++i) {
		scanf("%lld", &a[i]);
	}
	sort(a + 1, a + n + 1);
	for (int i = 1; i <= n; ++i) {
		c[i] = min(c[i - 1], a[i] - i);
		d[i] = d[i - 1] + a[i] - i;
	}
	for (int i = 1; i <= q; ++i) {
		scanf("%lld", &b[i]);
		if (b[i] <= n) {
			ans[i] = c[b[i]] + b[i] + 1;
			if (b[i] < n) {
				ans[i] = min(ans[i], a[b[i] + 1]);
			}
			continue;
		}
		if ((b[i] ^ n) & 1) {
			qq[++m] = node(b[i], i);
		}
	}
	dfs1(-1e10, 1e10, 1, m);
	m = 0;
	for (int i = 1; i <= q; ++i) {
		if (b[i] > n && !((b[i] ^ n) & 1)) {
			qq[++m] = node(b[i], i);
		}
	}
	dfs2(-1e10, 1e10, 1, m);
	for (int i = 1; i <= q; ++i) {
		printf("%lld ", ans[i]);
	}
}

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

posted @ 2023-05-19 16:43  zltzlt  阅读(41)  评论(0编辑  收藏  举报