题意:
给你一些数,让你找出几个数,使得这些数的平均数-中位数最大,问你这些数是哪些。
题解:
奇数:
奇数的话,中位数便非常好确定了,那么我们肯定是让排序后中位数右边的数尽可能大,并且左边的数也尽可能大,这样的话平均数也会变大。如果我们要枚举每个数是中位数的话,再枚举长度就是n^2的时间复杂度了,所以要优化。但是我们发现,随着长度变长,新加进来的两个数的和一定是越来越小的:
所以这个函数的曲线是这样的:
三分的图,但是对于诶个点是整数的时候,可以用二分代替三分:比较mid和mid-1的大小即可。
但是不能直接求平均数,会有精度的问题,所以用交叉相乘即可。
偶数
对于偶数的情况,我们考虑这是否可能,因为如果不可能的话就不用做了哈哈
首先我们假设左边的中位数是al,右边的中位数是ar,数的数量是s,那么现在的答案是
如果将ar去掉,答案变成
用上面的-下面的,就变成了
用高数的那种什么方法,就是说一个数变小之后还是大于等于另一个数,那么这个数就大雨另一个数。
所以因为2是一定小于等于s*(s-1)的,所以我们假设右边变成
,那么就变成了
,那么可以看成
和
,那么变成
和
这两个数的比较,于是又变成
和
这两个数的比较,于是我们可以看出,ar是
的中位数(因为al已经被消掉了),所以这两个数就是平均数与中位数的大小比较,同时我们知道,平均数应该一定大于等于中位数,否则你所求的东西将毫无意义。于是我们可以得出,奇数一定比偶数更优。
于是不用考虑偶数的情况。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=2e5+5;
ll a[N],pre[N];
int n;
ll check(int p,int len)
{
return pre[p]-pre[p-len-1]+pre[n]-pre[n-len];
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
sort(a+1,a+1+n);
for(int i=1;i<=n;i++)
pre[i]=pre[i-1]+a[i];
int p=1,len=0;
ll ans=0;
for(int i=1;i<=n;i++)
{
int l=1,r=min(i-1,n-i),mid;
while(r>=l)
{
mid=l+r>>1;
if((ll)check(i,mid)*(mid*2-1)>=(ll)check(i,mid-1)*(mid*2+1))
{
l=mid+1;
if((ll)check(i,mid)*(len*2+1)-a[i]*(len*2+1)*(mid*2+1)>(ll)ans*(mid*2+1)-a[p]*(len*2+1)*(mid*2+1))
ans=check(i,mid),p=i,len=mid;
}
else
r=mid-1;
}
}
printf("%d\n",len*2+1);
for(int i=p-len;i<=p;i++)
printf("%d ",pre[i]-pre[i-1]);
for(int i=n-len+1;i<=n;i++)
printf("%d ",pre[i]-pre[i-1]);
printf("\n");
return 0;
}