题解 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;
}

posted @ 2022-07-01 22:02  Saintex  阅读(29)  评论(0编辑  收藏  举报