「解题报告」CF1329E Dreamoon Loves AA
好题。
首先可以把题意转化一下,我们先把每相邻两个 A 的距离写成一个数组,然后对这个数组进行考虑。那么我们每改一个数,实际上就是将这个数组中的一个数分成两个数,我们要求的就是把这个数组分成 \(K = k + m + 1\) 个数,最小化极差。
首先不难得出一点,就是每个数最后肯定是被均分成若干份一定最优,也就是一个数 \(a_i\) 一定会被分成 \(k_i\) 份,满足每一份为 \(\lfloor\frac{a_i}{k_i} \rfloor\) 或 \(\rceil\frac{a_i}{k_i} \rceil\)。那么我们实际上要做的,就是找一个数组 \(k_i\) 满足 \(\sum k_i = K\),最小化 \(\max\{\lceil\frac{a_i}{k_i}\rceil\} - \min\{\lfloor\frac{a_i}{k_i}\rfloor\}\)。
我们不妨先把这个式子两部分拆开,求出一个答案的下界,也就是分别找出 \(\max\{\lceil\frac{a_i}{k_i}\rceil\}\) 和 \(\min\{\lfloor\frac{a_i}{k_i}\rfloor\}\) 的最小值和最大值。这两个是可以通过二分找到的,我们设两个值为 \(R, L\)。
如果我们最后的值域为 \([l, r]\),说明我们要满足 \(\lceil\frac{a_i}{k_i}\rceil \le r\) 且 \(\lfloor\frac{a_i}{k_i}\rfloor \ge l\),化下不等式就能得到 \(\lceil\frac{a_i}{r}\rceil \le k_i \le \lfloor\frac{a_i}{l}\rfloor\)。于是不难得出,只需要满足 \(\lceil\frac{a_i}{r}\rceil \le \lfloor\frac{a_i}{l}\rfloor\),且 \(\sum \lceil\frac{a_i}{r}\rceil \le k \le \sum \lfloor\frac{a_i}{l}\rfloor\),那么这个值域 \([l, r]\) 就一定对应着一组合法的 \(\{k_i\}\)。
那么我们再去考虑原来的 \([L, R]\) 的值域能否取到。不难得出 \(\sum \lceil\frac{a_i}{R}\rceil \le k \le \sum \lfloor\frac{a_i}{L}\rfloor\) 是一定成立的。我们记此时的 \(\lceil\frac{a_i}{R}\rceil = dl_i, \lfloor\frac{a_i}{L}\rfloor = dr_i\)。那么,假如我们满足 \(dl_i \le dr_i\),那么这个下界就是可以取到的。假如 \(dl_i > dr_i\),那么我们就必须增大 \(R\) 或减小 \(L\) 了。而我们再仔细分析,由于 \(dr_i - dl_i = \lfloor\frac{a_i}{L}\rfloor - \lceil\frac{a_i}{R}\rceil \ge \lfloor\frac{a_i}{L}\rfloor - \lfloor\frac{a_i}{R}\rfloor - 1 \ge -1\),那么也就是说,即使 \(dl_i > dr_i\),那么它们的差不会超过 \(1\)。那么,我们就只需要令 \(dl_i\) 减小 \(1\),或令 \(dr_i\) 增加 \(1\),就能满足条件了。而通过这个我们可以计算出新的 \(L, R\) 的值,那么对于每一个 \(dl_i > dr_i\),我们实际上需要做的是将 \(L\) 减小至 \(L_i\) 或将 \(R\) 增大至 \(R_i\)。这个就很好做了,我们只需要枚举 \(L\) 最后减小至了多少,对 \(L\) 排序后,对 \(R\) 做一个前缀最大值即可。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 400005;
int T;
int m;
long long n, k;
long long a[MAXN];
int main() {
scanf("%d", &T);
while (T--) {
scanf("%lld%d%lld", &n, &m, &k);
long long lst = 0;
for (int i = 1; i <= m; i++) {
long long x; scanf("%lld", &x);
a[i] = x - lst;
lst = x;
}
a[++m] = n - lst;
k += m;
long long l = 1, r = 1000000000000000;
while (l < r) {
long long mid = (l + r + 1) >> 1;
long long p = 0;
for (int i = 1; i <= m; i++) {
p += a[i] / mid;
}
if (p >= k) l = mid;
else r = mid - 1;
}
long long L = l;
l = 1, r = 1000000000000000;
while (l < r) {
long long mid = (l + r) >> 1;
long long p = 0;
for (int i = 1; i <= m; i++) {
p += (a[i] + mid - 1) / mid;
}
if (p <= k) r = mid;
else l = mid + 1;
}
long long R = r;
vector<pair<long long, long long>> vec;
for (int i = 1; i <= m; i++) {
long long dl = a[i] / L, dr = (a[i] + R - 1) / R;
if (dr > dl) {
vec.push_back({ a[i] / (dl + 1), dr == 1 ? LLONG_MAX / 10 : (a[i] + (dr - 1) - 1) / (dr - 1) });
}
}
sort(vec.begin(), vec.end());
long long ans = LLONG_MIN;
for (auto p : vec) {
ans = max(ans, p.first - R);
R = max(R, p.second);
}
ans = max(ans, L - R);
printf("%lld\n", -ans);
}
return 0;
}