CodeForces 1832D2 Red-Blue Operations (Hard Version)
首先,如果一个点变成蓝色,在下一次立刻把它变成红色最优。这样对这个点造成的影响是减 \(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;
}