JZOJ 6899. 【2020.11.27提高组模拟】第三题(排序+均值不等式)

JZOJ 6899. 【2020.11.27提高组模拟】第三题

题解

  • 题意可以转化为给定一个序列 a a a,求一个由非负数构成的序列 b b b,使得 ∑ b i = C sum b_i=C bi=C ∑ ( a i − b i ) 2 sum (a_i-b_i)^2 (aibi)2最小。

  • 由均值不等式或尝试得出,当所有 a i − b i a_i-b_i aibi都相等时,可以使得 ∑ ( a i − b i ) 2 sum (a_i-b_i)^2 (aibi)2最小,但是这样并不能保证每个 b i b_i bi都是正数,把 a i a_i ai按升序排好后,前面可能会有一段对应的 b i b_i bi全都为 0 0 0,而后面的都满足 a i − b i a_i-b_i aibi都相等。

  • 那么这段 0 0 0的长度怎么求得?

  • 直接枚举 0 0 0的长度有多长,每种合法的情况取最小值即可。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100010
#define ll long long
#define ld long double
ll f[N], g[N], s[N][2];
struct num {
	ll x; int id;
}a[N];
ll gcd(ll x, ll y) {
	return y ? gcd(y, x % y) : x;
}
int cmp(num x, num y) {
	return x.x < y.x;
}
int main() {
	int n, C, i;
	scanf("%d%d", &n, &C);
	for(i = 1; i <= n; i++) scanf("%lld", &a[i].x), a[i].id = i;
	sort(a + 1, a + n + 1, cmp);
	f[0] = 0;
	for(i = 1; i <= n; i++) f[i] = f[i - 1] + a[i].x * a[i].x;
	g[n + 1] = 0;
	for(i = n; i; i--) g[i] = g[i + 1] + a[i].x;
	ld ans = -1; int p;
	for(i = 0; i < n; i++) {
		ld t = (g[i + 1] - C) / (ld)(n - i);
		if((ld)a[i + 1].x - t >= 0 && (ans == -1 || f[i] + t * t * (n - i) < ans)) ans = f[i] + t * t * (n - i), p = i;
	}
	for(i = 1; i <= p; i++) s[a[i].id][0] = 0, s[a[i].id][1] = 1;
	for(i = p + 1; i <= n; i++) {
		s[a[i].id][0] = a[i].x * (n - p) - g[p + 1] + C, s[a[i].id][1] = n - p;
	}
	for(i = 1; i <= n; i++) {
		if(s[i][1] == 1) printf("%d
", s[i][0]); 
		else {
			int g = gcd(s[i][0], s[i][1]);
			if(g == s[i][1]) printf("%d
", s[i][0] / g);
			else printf("%d/%d
", s[i][0] / g, s[i][1] / g);
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/LZA119/p/14279488.html