CodeForces

来源:互联网 发布:网络订餐食品安全 编辑:程序博客网 时间:2024/06/05 00:14

题目链接


题意:


给定一个n个数序列,选取一些数,使得这些数的平均数减中位数尽可能大。如有多个结果,随意输出一个。


思路:


首先先上神结论:

    要想使平均数-中位数尽可能的大,则选择的数的个数一定为奇数个,偶数个一定不会比他更优.


不太对的证明?

先进行排序 
选数时有两种情况,奇数个和偶数个,分别来看,设:

奇数时 u是平均数, n个数 , x1是中位数 
偶数时 ((n*u)+x2)/(n+1)平均数 ,n+1个数, (x1+x2)/2 是中位数 
平均数大于中位数 u>x1,偶变奇时 平均数的变化量:(x2-u)/(n+1) , 中位数变化量:(x2-x1)/2 ,

中位数的变化量大于平均数的变化量。 
所以当选偶数个时我们舍弃掉中位数中较大的一个,所得的结果一定比选偶数个优,所以一定选择奇数个数。 



PS :

我们知道选数的个数一定为奇数个之后,就要考虑要怎么求了.

首先可以考虑,当选出的数的个数很少,则平均值-中位数 越来越小,当选取的数很多的时候 平均-中位数也越来越小.

那么二者中间一定存在某个位置有极大值.这就满足了三分的性质,题目也正好要让我们求最大的.所以可以考虑三分.

     三分的话,我们还是三分区间然后check.我们枚举每个数来当中位数,然后求平均数.中位数确定了所以我们要让平均值尽量打,那么在中位数左面的数应该尽可能选择靠近中位数的数,在中位数右边的数应该选择尽可能原理中位数的数,以此来求平均值.这个过程可以考虑维护一个前缀和来得到,因为除法会有精度损失,所以我们把最后结果都乘上一个数的个数,转化为整数来做.剩下的就很简单了.

#include<iostream>#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>#include<set>#include<vector>#include<queue>#include<map>#define inf 0x3f3f3f3fusing namespace std;typedef long long ll;const ll mod=1e9+7;const int maxn=2e5+10;ll sum[maxn],a[maxn];int n; ll cal(int len,int pos){ll res = sum[n] - sum[n-len]+sum[pos]-sum[pos-len-1];return res;}int main(){while(~scanf("%d",&n)){sum[0] = 0;ll len = 0,mid = 1,mm = 0;for(int i = 1;i <=n ;i++)scanf("%lld",&a[i]);sort(a+1,a+1+n);for(int i = 1;i <=n ;i++)sum[i] = sum[i-1]+a[i];for(int i = 2;i <=n-1;i++){int l=0,r=min(i-1,n-i),lmid,rmid;while(l<=r){lmid = l + (r-l)/3;rmid = r - (r-l)/3;ll s1 = cal(lmid,i);ll s2 = cal(rmid,i);if(s1*(2*rmid+1) < s2*(2*lmid +1))l = lmid + 1;elser = rmid - 1;}ll res = cal(l,i) - (2*l+1)*a[i];if(res*(2*len+1)>mm*(2*l+1)){mm = res;len = l;mid = i;}}printf("%lld\n",2*len+1);for(int i = mid-len;i <= mid;i++)printf("%lld ",a[i]);for(int i = n-len +1;i <= n;i++)printf("%lld ",a[i]);puts("");}return 0;}