把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

CF626E Simple Skewness

题目链接

题目解析

首先考虑固定一个中位数,来使平均数最大。

我们可以将这\(n\)个数排序,然后从中位数的地方左右拓展,根据贪心,大的数肯定是要选的,所以中位数以前中间不会\(skip\)掉某些数然后选别的,肯定是挨着往前选,而中位数以后肯定是从最大的地方倒着选过来。这个过程中我们发现平均数先变大后变小。这个很好理解:刚开始加入的数都比较大,后来加入的数都越来越小,在某一时刻会使平均数变小。

那么这是个单峰函数,可以三分加入的数的个数,不过要注意中间可能会出现一段与\(x\)平行的一段。

值域是\(1e6\)级别的,但是我们要相信\(cf\)优秀的评测速度和它\(3s\)的时限(雾

不过可以证明中位数一定在原\(n\)个数之中,节省掉大量枚举中位数的时间。这个结论换句话说说,也就是选出来的数的个数一定是奇数的时候比较优。

简单证明:

不妨设一个奇数长度的答案序列为\(a[1...2k-1]\)(已排序),并且他们的平均数为\(av\)。现在证明向这个序列中加入一个数,答案不会更优。根据之前的说法,新加入的数如果小于中位数,那么它小于\(a[1]\);如果它大于中位数,那么它满足\(a[k]<=x<=a[k+1]\),那么这两种情况在加入有序序列后为\(a[0]/a[k+1]\)

先讨论\(a[k+1]\)的情况:新的中位数为\(\frac{a[k]+a[k+1]}2\),会变大。而由于这是设的一个答案序列,所以是在中位数为\(a[k]\)的情况下的平均值的最大值,那么\(a[k+1]\)小于平均值,否则它应该在原答案序列中,那么新的平均数会变小。所以答案会变小。

对于\(a[0]\)的情况:增加\(a[0]\)后,由于\(a[0]\)是一个极小值,所以对平均数的影响要大于对中位数的影响(平均值易受极端值的影响),所以平均值减少得比中位数快,那么答案会变小。

(怎么感觉都好玄乎,感性理解\(2333\)

但其实没有这个结论似乎也不需要枚举完值域,枚举\(2n-1\)个数就可以了吧(每个数,和每相邻两个数的平均值


►Code View

#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
#define N 200005
#define MOD 998244353
#define INF 0x3f3f3f3f3f3f3f3f
#define LL long long
LL rd()
{
	LL x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48); c=getchar();}
	return f*x;
}
int n,a[N];
LL s[N];
double mx=-INF;
double f(int pos,int len)
{
	LL sum=s[pos]-s[pos-len-1]+s[n]-s[n-len];
	double av=1.0*sum/(2.0*len+1.0);
	return av-a[pos];
}
pair<double,int> tri(int pos,int l,int r)
{
	while(l+3<r)
	{
		int lmid=l+(r-l)/3,rmid=r-(r-l)/3;
		double lans=f(pos,lmid),rans=f(pos,rmid);
		if(lans<rans) l=lmid;
		else r=rmid;
	}
	pair<double,int> res=make_pair(0,0);
	
	for(int i=l;i<=r;i++)
	{
		double tmp=f(pos,i);
		if(tmp>res.first)
			res=make_pair(tmp,i);
	}
	return res;
}
int main()
{
	n=rd();
	for(int i=1;i<=n;i++)
		a[i]=rd();
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++)
		s[i]=s[i-1]+a[i];
	
	pair<int,int> ans=make_pair(0,0);//选一个 为0 (排除掉负数 
	for(int i=1;i<=n;i++)
	{
		pair<double,int> res=tri(i,0,min(i-1,n-i));
		if(res.first>mx)
		{
			mx=res.first;
			ans=make_pair(i,res.second);
		}
	}
	
	printf("%d\n",ans.second*2+1);
	for(int i=ans.first-ans.second;i<=ans.first;i++)
		printf("%d ",a[i]);
	for(int i=n-ans.second+1;i<=n;i++)
		printf("%d ",a[i]);
	return 0;
}
/*

*/
posted @ 2020-12-03 15:02  Starlight_Glimmer  阅读(79)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end