http://codeforces.com/contest/626/problem/E
题意: 给你n个数,让你选一个非空子集,使得子集中所有数的平均数减去 中位数 这个值最大。
排序后,枚举每一个数位中位数,
对于第i个数作为中位数的情况,我们要使得平均数尽可能大,就是在i的左右对称选x个数,显然越大越好,左边选的是靠近i,右边选的是靠近n。
如何在logn内选出这个X:
如果x=1;也就是选 a[i-1] 和 a[n] 会使得平均数增大,那么当x=2时,显然 a[i-2] 与a[n-1]的和比上一对要小,但是如果他们仍大于2*当前平均数的话,仍然可以拉高平均数,每次拉高的量越来越少,由于是有序的,每次的
a[i-k]+a[n-k+1]的和都是逐渐变小,而平均数是渐渐变大的。
可以知道 随着x的增大,平均数的值应该是 先单调增加 后 单调减少。【也就是所谓bitonic 双调的】,本来应该用三分,但是由于都是整点,我们可以二分找到那个使得平均数最大的X即可
二分的判断就是,判断avg(X)>avg(x-1) 即可
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <iostream>
using namespace std;
__int64 mod=1000000000+7;
__int64 min(__int64 a,__int64 b)
{return a<b?a:b;}
__int64 max(__int64 a,__int64 b)
{return a>b?a:b;}
__int64 n,m;
__int64 tm[200005];
__int64 s[200005];
double eps=1e-8;
__int64 fz,fm;
__int64 bin(__int64 i,__int64 x)
{
__int64 ll=s[i-1]-s[i-x-1];
__int64 rr=s[n]-s[n-x];
fz=ll+rr+tm[i];
fm=2*x+1;
__int64 last_fz=s[i-1]-s[i-1-(x-1)]+s[n]-s[n-(x-1)]+tm[i];
__int64 last_fm=2*x-1;
if (fz*last_fm-last_fz*fm>0) return 1; //避免double丢失精度
else return 0;
}
int main()
{
__int64 i,j;
scanf("%I64d",&n);
for (i=1;i<=n;i++)
scanf("%I64d",&tm[i]);
sort(tm+1,tm+1+n);
for (i=1;i<=n;i++)
s[i]=s[i-1]+tm[i];
__int64 ansfz,ansfm=0;
__int64 ans_i=0;
__int64 ans_x=0;
for (i=1;i<=n;i++)
{
__int64 l=0;
__int64 r=min(i-1,n-i);
__int64 x;
while(l<=r)
{
if (r-l<=1)
{
if (bin(i,r))
x=r;
else
x=l;
bin(i,x);//记得要更新fz,fm
break;
}
__int64 mid=(l+r)>>1;
if (bin(i,mid))
l=mid;
else
r=mid-1;
}
fz-=fm*tm[i];
if (ansfm==0||fz*ansfm-fm*ansfz>0 )
{
ansfz=fz;
ansfm=fm;
ans_i=i;
ans_x=x;
}
}
if (ans_i==0)
{
printf("1\n");
printf("%I64d\n",tm[1]);
return 0;
}
printf("%I64d\n",ans_x*2+1);
printf("%I64d",tm[ans_i]);
for (i=ans_i-1;i>=ans_i-ans_x;i--)
printf(" %I64d",tm[i]);
for (i=n;i>=n-ans_x+1;i--)
printf(" %I64d",tm[i]);
printf("\n");
return 0;
}