Galaxy

Galaxy

在一维坐标轴上给出n个点,第i个点坐标为\(x_i\),现在你可以任意移动k个点的,最小化它们的方差,\(n\leq 50000\)

感觉以前写的太乱了,补一篇可以供快速阅读的题解

首先方差有个公式\(\sum_{i=1}^n(x_i-\bar{x})^2=\sum_{i=1}^nx_i^2-n\bar{x}^2\),假设我们已经确定了移动那些点,假设移动的是\(x_1,x_2,...,x_k\),那么要让方差最大,就相当于是一个多元函数求最值的问题,设\(F(x_1,...,x_k)=\sum_{i=1}^nx_i^2-n\bar{x}^2\),结论是关于各个变量的偏导为\(0\)的时候取到最值,也就是对于任意一个\(j\)\(x_j=\dfrac{\sum_{i=1,i\ne j}^n}{n-1}\),如果\(k=1\),那么这个点恰好是在剩下点的平均数取到,因此可以猜测这\(k\)个点都是在剩下点的平均数位置,证明参考原来的版本就可以了。

然后答案就是\(\sum_{i=1}^nx_i^2-n\bar{x}^2=\sum_{i=k+1}^nx_i^2-(n-k)\bar{x}^2\),这个恰好就是剩下\(n-k\)个数的方差,最小化,这要让他们坐标连续即可,这样就可以做到\(O(n^2)\),考虑枚举右端点,看左端点套路,预处理平方和和前缀和,就可以做到\(O(n)\)了。


\(\bar{x}=\frac{\sum_{i=1}^nx_i}{n}\),容易知道方差为

\[\sum_{i=1}^n(x_i-\bar{x})^2=\sum_{i=1}^nx_i^2-n\bar{x}^2 \]

对于只移动一个点坐标x来看,显然可以看成一个二次函数,即

\(f(x)=\sum_{i=1}^n{x_i}^2-n\bar{x}^2\)

因为化简过于复杂,对它进行求导,这样与x无关的项都可以丢掉了

\[f(x)'=(\sum_{i=1}^n{x_i}^2-n\bar{x}^2)'=2x-n(\bar{x}^2)'=2x-n2\bar{x}(\bar{x})'= \]

\[2x-2\bar{x}(\sum_{i=1}^nx_i)'=2x-2\bar{x} \]

容易知道随着x的增加,这个导函数是在单调递增的,因此它存在最小值,显然是在驻点处取到,故令\(f(x)'=0\),有

\(x=\bar{x}\Rightarrow x=\frac{\sum_{i=1}^nx_i-x}{n-1}\)

于是对于只动一个坐标x而言,x取到除x以外的点的平均值所在的点就是答案,我们还可以得到一个结论,往数的集合中加入它的平均数,不改变新的集合的平均数。

但是我们需要讨论k个点的情况,于是对于每个点\(x_i\)我们可以有(不妨假设这k个点为\(x_1,x_2,..,x_k\)),那么分别有

\[x_1=\frac{\sum_{i=1}^nx_i-x_1}{n-1},x_2=\frac{\sum_{i=1}^nx_i-x_2}{n-1}... \]

\[x_k=\frac{\sum_{i=1}^nx_i-x_k}{n-1} \]

累加起来有

\[(n-1)\sum_{i=1}^kx_i=k\sum_{i=1}^nx_i-\sum_{i=1}^kx_i \]

也就是

\[n\sum_{i=1}^kx_i=k\sum_{i=1}^nx_i \]

也就是

\[\frac{\sum_{i=1}^kx_i}{k}=\frac{\sum_{i=1}^nx_i}{n} \]

于是我们得到了k个要动的数的平均值恰好为n个数的平均值,继续变

\[n\sum_{i=1}^kx_i=k\sum_{i=1}^nx_i\Rightarrow n\sum_{i=1}^kx_i=k\sum_{i=1}^kx_i+k\sum_{i=k+1}^nx_i \]

\[(n-k)\sum_{i=1}^kx_i=k\sum_{i=k+1}^nx_i \]

也就是

\[\frac{\sum_{i=1}^kx_i}{k}=\frac{\sum_{i=k+1}^nx_i}{n-k} \]

整理起来也就有

\[\frac{\sum_{i=1}^kx_i}{k}=\frac{\sum_{i=k+1}^nx_i}{n-k}=\frac{\sum_{i=1}^nx_i}{n} \]

所以可以得出一个很棒的结论,向一个集合中加入若干个平均数为改个集合的平均数,平均数不变,原来的集合的平均数等于加进的平均数等于新集合的平均数,而且满足两个相等,可以推出三个相等。

现在考虑k个数的方差,方差显然转换成下式比较好研究

\[\sum_{i=1}^nx_i^2-n\bar{x}^2 \]

其余n-k个数字是常量(包括平均数),拆开有

\[\sum_{i=1}^kx_i^2+\sum_{i=k+1}^{n}x_i^2-n\bar{x}^2 \]

于是要让整个式子最小化,也就是最小化

\[\sum_{i=1}^nx_i^2=x_1^2+x_2^2+...+x_k^2 \]

根据均值不等式,容易知道最小化这个式子,要满足\(x_1=x_2=...=x_k\)

而我们有\(\frac{x_1+x_2+...+x_k}{k}=\frac{kx_1}{k}=x_1=\bar{x}\),于是有

\[x_1=x_2=...=x_k=\bar{x}=\frac{\sum_{i=k+1}^{n-k}x_i}{n-k} \]

也就是对于这k个数字,我们只要都放到剩下的数字平均数所在位置即可,而根据方差定义式。

\[\sum_{i=1}^n(x_i-\bar{x})^2 \]

容易知道,此时这k个数字所产生的影响为0,因为\(x_i=\bar{x}(i=1,2,...,k)\)

于是我们只要考虑剩下n-k个数字的方差即可,而根据方差的实际含义,数字的离散程度,于是剩下n-k个数字要尽可能集中,于是我们只需要将所有坐标排序,取连续的长度为\(n-k\)的区间,把它们的方差取max即可,为了快速求出方差,我们根据推广式

\[\sum_{i=1}^nx_i^2-n\bar{x}^2 \]

\(\bar{x}=\frac{\sum_{i=1}^nx_i}{n}\),因此我们只要维护平方的前缀和和普通的前缀和,带入这个式子计算,就可以做到\(O(1)\)查询区间的方差了,整个时间复杂度仅有\(O(n)\)

参考代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#define il inline
#define ri register
#define lb double
#define Size 50500
using namespace std;
lb a[Size],sum[Size],sum2[Size];
int main(){
	int lsy,n,k;scanf("%d",&lsy);
	while(lsy--){
		scanf("%d%d",&n,&k);
		for(int i(1);i<=n;++i)
			scanf("%lf",&a[i]);
		if(n==k){printf("%.11lf\n",(lb)0);continue;}
		sort(a+1,a+n+1);lb ans(1e100);k=n-k;
		for(int i(1);i<=n;++i)
			sum[i]=sum[i-1]+a[i],
				sum2[i]=sum2[i-1]+a[i]*a[i];
		for(int i(k);i<=n;++i)
			ans=min(ans,sum2[i]-sum2[i-k]-k*pow((sum[i]-sum[i-k])/k,2));
		printf("%.11lf\n",ans);
	}
	return 0;
}
posted @ 2019-08-25 17:46  a1b3c7d9  阅读(150)  评论(0编辑  收藏  举报