​C - Maximize GCD​

给定长度为 n , ( 2 ≤ 3 × 1 0 5 ) n, (2 \le 3 \times 10 ^ 5) n,(2≤3×105)的数组 a , ( 1 ≤ a i ≤ 3 × 1 0 5 ) a, (1 \le a_i \le 3 \times 10 ^ 5) a,(1≤ai​≤3×105),一个数字 K , ( 1 ≤ K ≤ 1 0 18 ) K, (1 \le K \le 10 ^{18}) K,(1≤K≤1018),

我们可以对数组 a a a进行最多 k k k次操作,每次操作选定一个 i , ( 1 ≤ i ≤ n ) i, (1 \le i \le n) i,(1≤i≤n)使 a i + 1 a_i + 1 ai​+1,为数组最大的可能 g c d gcd gcd是多少。

我们设 m a x n = m a x ( { a 1 , … , a n } ) maxn = max(\{a_1, \dots, a_n\}) maxn=max({a1​,…,an​}),如果最后的答案 a n s ans ans大于等于 m a x n maxn maxn,则一定有 i ∈ [ 1 , n ] , a i = a n s i \in [1, n], a_i = ans i∈[1,n],ai​=ans。

否则我们假设答案为 x x x,则判断是否合法:

∑ i = 1 n ⌈ a i x ⌉ × x − a i \sum_{i = 1} ^{n} \lceil\frac{a_i}{x}\rceil \times x - a_i\\ i=1∑n​⌈xai​​⌉×x−ai​

即上面那个式子求和是否小于等于 k k k,这个式子可以前缀和,无穷级数的复杂度算一下就好了,整体复杂度最高 m a x n log ⁡ m a x n maxn \log maxn maxnlogmaxn。

#include <bits/stdc++.h>

using namespace std;

const int N = 3e5 + 10;

int a[N], cnt[N], n, maxn;

long long sum[N], k, s;

int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
cin >> n >> k;
for (int i = 1; i <= n; i++) {
cin >> a[i];
s += a[i], cnt[a[i]]++, sum[a[i]] += a[i];
maxn = max(maxn, a[i]);
}
if (1ll * maxn * n - s <= k) {
// x * n - sum <= k -> x * n <= k + sum -> x = (k + sum) / n;
cout << (k + s) / n << "\n";
return 0;
}
for (int i = 1; i <= maxn; i++) {
cnt[i] += cnt[i - 1];
sum[i] += sum[i - 1];
}
int ans = 1;
for (int i = 2; i < maxn; i++) {
long long cur = 0;
for (int j = i; j <= maxn; j += i) {
// [j - i + 1, j] -> i
cur += 1ll * (cnt[j] - cnt[j - i]) * j - (sum[j] - sum[j - i]);
}
if (maxn % i) {
int l = maxn / i * i, r = maxn;
cur += 1ll * (cnt[r] - cnt[l]) * (maxn / i + 1) * i - (sum[r] - sum[l]);
}
if (cur <= k) {
ans = i;
}
}
cout << ans << "\n";
return 0;
}