题目链接:http://codeforces.com/problemset/problem/626/E
题目大意:有k个数,选若干个数,使这几个数的平均值-中位数最大,输出任意一种方案
先排序从小到大
然后枚举中位数,当然这个序列是包含奇数个数,为什么不是偶数个数,因为当是偶数的时候可以把中间两个数大的那个删掉,这样解会更优
证明如下:
不想写证明,以后再补
然后,对于平均数-中位数,随着区间长度增长,值会先增加后减少,需要找到峰值,可以使用三分,我是使用的是二分判断增减性来更新,原理上和三分类似
就是判断mid和mid+1的大小,以此来判断增减性
给定的中位数,需要时平均数尽可能的大所以找中位数左边的若干个,和序列最右边的若干个
代码如下:
#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
long long s[200050];
long long t[200050];
int n;
int ansi=1,ansl=0;
double ans=-1;
double now1,now2,nowans;
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%I64d",&t[i]);
}
sort(t+1,t+n+1);
s[0]=0;
for (int i=1;i<=n;i++)
{
s[i]=t[i]+s[i-1];
}
int l,r,mid;
int now;
for (int i=2;i<n;i++)
{
l=1;
r=min(i-1,n-i);
now=0;
while (r>=l)
{
mid=(l+r)>>1;
now1=1.0*(s[n]+s[i]-s[n-mid]-s[i-mid-1])/(2*mid+1);
now2=1.0*(s[n]+s[i]-s[n-mid+1]-s[i-mid])/(2*mid-1);
if (now1>now2)
{
now=mid;
l=mid+1;
}
else
{
r=mid-1;
}
}
nowans=1.0*(s[n]+s[i]-s[n-now]-s[i-now-1])/(2*now+1)-t[i];
if (ans<nowans)
{
ans=nowans;
ansi=i;
ansl=now;
}
}
printf("%d\n",2*ansl+1);
for (int i=ansi-ansl;i<=ansi;i++)
{
printf("%I64d ",t[i]);
}
for (int i=n-ansl+1;i<=n;i++)
{
printf("%I64d ",t[i]);
}
printf("\n");
return 0;
}