本体是前缀和加二分,又包含有贪心的思想。首先对数据排序,初始化前缀和的同时二分查找更新出ans值。
本题难点在于证明对所有前缀所对应的子集中一定会包含有正解子序列。
证明:
我们不取第一个数,在剩余数中任取一个可以得到正解的子序列,我们将这个子序列加上第一个数,势必会降低整个子序列的平均值(在序列中插入一个小于平均值的数会降低序列的平均值),原序列中大于平均值的数字个数不变,正解不受影响。相应的不取任何一个前缀,在剩余数中任取一个可以得到正解的子序列,我们将这个子序列加上他的前缀,势必会降低整个子序列的平均值(因为原数组有序,前缀的平均值必然小于后面任何一个子序列的平均值),原序列中大于平均值的数字个数不变,正解不受影响。我们再用贪心的思想来分析,如果我们在的到的一个子序列加上一个前缀,降低了整体平均值,就会使得序列中的数字会有更大可能性大于平均值,更有可能的到正解,分析可以发现,包含有正解的子序列一定被包含与所有的前缀。
解法
#include<iostream>
#include<queue>
#include<math.h>
#include<algorithm>
#include<cstring>
#include<vector>
#include<map>
#include<set>
const int N = 1e6 + 10;
const int M = 4 * N;
#define mod 192600817
#define int long long
#define FAST ios::sync_with_stdio(false);
#define endl '\n'
#define rep(i,n)for(int i=1;i<=n;++i)
#define sc(n) scanf("%d",&n);
using namespace std;
const int INF = 1e16;
typedef pair<int, int>PII;
int a[N];
int binary_search(int l, int r, int val)
{
while (l < r)
{
int mid = l + r + 1 >> 1;
if (a[mid] <= val)l = mid;
else r = mid - 1;
}
return l;
}
signed main()
{
FAST;
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
sort(a + 1, a + 1 + n);
int sum = 0,ans=0;
double v;
sum = a[1];
for (int i = 2; i <= n; i++)
{
sum += a[i];
v = sum / i;
//查找当前前缀范围内大于平均数的第一个数的下标
int p = binary_search(1, i, v);
//前缀数字的数量减去下标即为大于平均数的数的个数,max更新即可
ans = max(ans, i - p );
}
cout << ans << endl;
return 0;
}