题解 CF1344D Résumé Review
lnk
此题分为两个 Part,对我而言第一个 Part 更难想(得多)。
\(\mathrm {Part\ 1}\)
分析 \(\sum_{i=1}^n b_i(a_i-b_i^2)\)。
考虑我们是确定 \(b_i\) 的值,而 \(\sum b_i=k\) 是一个定值。此处有一个 trick 就是一个一个加,把 \(b_i\) 看成 \(b_i\) 个 \(1\)。此处不妨探讨加上一个 \(1\) 有什么影响。
\(\sum_{i=1}^n b_i(a_i-b_i^2)-\sum_{i=1}^n (b_i-1)(a_i-(b_i-1)^2)=(-2b_i^2+2b_i)+(a_i-1)\)。
对称轴 \(x=-\frac 1 2\)。因为 \(b_i\geqslant 0\),所以差值随 \(b_i\) 的增大而减小。
根据类似的贪心(超级钢琴),暴力做,放进优先队列里再取出来的复杂度是 \(\mathcal {O}(klog_2 n)\)。
\(\mathrm {Part\ 2}\)
发现我们不用一次一次枚举差值,直接二分这个差值即可(当然也可以直接解一个二次方程),再套个二分统计个数,就做完了。
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define LL long long
#define uint unsigned int
using namespace std;
const int MAXN = 1e5 + 5;
int n;
LL k, a[MAXN], ans, c[MAXN], d[MAXN];
bool check(__int128 x) {
ans = 0;
for(int i = 1; i <= n; i ++) {
int l = 1, r = a[i], mid, res = 0;
while(l <= r) {
mid = (l + r) >> 1;
if(mid * (a[i] - (__int128)mid * mid) - (mid - 1) * (a[i] - (__int128)(mid - 1) * (mid - 1)) >= x) res = mid, l = mid + 1;
else r = mid - 1;
}
ans += res; c[i] = res;
if(ans >= k) return 1;
}
return 0;
}
int main() {
scanf("%d%lld", &n, &k);
for(int i = 1; i <= n; i ++) scanf("%lld", &a[i]);
__int128 l = -1e35, r = 1e35, mid, res;
while(l <= r) {
mid = (l + r) >> 1;
if(check(mid)) l = mid + 1, res = mid;
else r = mid - 1;
}
check(res);
for(int i = 1; i <= n; i ++) d[i] = c[i];
check(res + 1); LL rst = k - ans;
for(int i = 1; i <= n; i ++) {
if(d[i] - c[i] >= rst) printf("%lld ", c[i] + rst), rst = 0;
else rst -= (d[i] - c[i]), printf("%lld ", d[i]);
}
return 0;
}