思路
首先你发现假设当前的平均数是 \(a\) , 其中 \(\lceil a \rceil = k\) , 那么你势必要选上所有 \(< k\) 的数来拉低平均数, 然后贪心的从小到大选 \(\geq k\) 的数来提高贡献
如果想不到也可以这样想, 对于一个确定的平均数, 一定要尽可能的让比平均数小的数更多, 才能更多的腾出空间给大于平均数的数
显然的, 这样子搞完之后, 每次选择的一定是一段排序后数列的前缀, 你对前缀做一点二分操作就可以计算出答案了
以下是赛时想歪之后的思路, 我看看还能不能救回来
赛时发现, 最优解可以看做把排序后的数列 (以下简称数列) 分成两段, 分别取两端的前缀组合就可以找到一个不劣的答案
其实容易发现, 每次假设把 \(a\) 数列分成两部分 \(u, v\) , 那么显然的, 如果你对于一个确定的 \(v\) 进行分析, \(u\) 一定是选完 \(v\) 之前的所有最优, 也就是说 \(u, v\) 显然连在了一起拼成了前缀
代码
没有难度, 脑子没转过来
#include <bits/stdc++.h>
// #define FILE_IO
#define int long long
const int MAXN = 1e6 + 20;int n;
int a[MAXN];class Brute_Force
{
private:int Ans = 0;public:void solve() {std::sort(a + 1, a + n + 1);for (int p = n; p >= 1; p--) {int rsum = 0;for (int r = p; r <= n; r++) {rsum += a[r];int lsum = 0; bool flag = false;for (int l = 1; l < p; l++) {lsum += a[l];if ((r - p + 1ll + l) * a[p] > lsum + rsum) {Ans = std::max(Ans, r - p + 1ll);}}}}printf("%lld", Ans);}
} BF;class Greedy
{
private:int binsearch(int l, int r, int sum, int num) {int Left = l, Right = r + 1;while (Left < Right) {int Mid = (Left + Right) >> 1;if (a[Mid] * num > sum) Right = Mid;else Left = Mid + 1;}return Left;}public:void solve() {std::sort(a + 1, a + n + 1);int sum = 0;int ans = 0;for (int i = 1; i <= n; i++) {sum += a[i];int res = binsearch(1, i, sum, i);ans = std::max(ans, i - res + 1);}printf("%lld", ans);}
} GR;signed main()
{
#ifdef FILE_IOfreopen("average5.in", "r", stdin);freopen("average5.out", "w", stdout);
#endifscanf("%lld", &n);for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);// if (n <= 500) BF.solve();GR.solve();return 0;
}
总结
平均数很好的性质, 贪心策略的应用
如果赛时思维想糊了考虑化枚举为单点的正确性, 对于简单题来说一般是这样的
最好是因为难受才没有做出来, 说不定就是菜