zoukankan      html  css  js  c++  java
  • Galaxy

    Galaxy

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

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

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

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


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

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

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

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

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

    [f(x)'=(sum_{i=1}^n{x_i}^2-nar{x}^2)'=2x-n(ar{x}^2)'=2x-n2ar{x}(ar{x})'= ]

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

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

    (x=ar{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=ksum_{i=1}^nx_i-sum_{i=1}^kx_i ]

    也就是

    [nsum_{i=1}^kx_i=ksum_{i=1}^nx_i ]

    也就是

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

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

    [nsum_{i=1}^kx_i=ksum_{i=1}^nx_iRightarrow nsum_{i=1}^kx_i=ksum_{i=1}^kx_i+ksum_{i=k+1}^nx_i ]

    [(n-k)sum_{i=1}^kx_i=ksum_{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-nar{x}^2 ]

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

    [sum_{i=1}^kx_i^2+sum_{i=k+1}^{n}x_i^2-nar{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=ar{x}),于是有

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

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

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

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

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

    [sum_{i=1}^nx_i^2-nar{x}^2 ]

    (ar{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
    ",(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
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    代码阅读:结构与逻辑
    ReactiveCocoa 中 RACSignal 所有变换操作底层实现分析(上)
    大白话讲解Promise(一)
    ReactiveCocoa 中 RACSignal 是如何发送信号的
    The Future Of ReactiveCocoa by Justin Spahr-Summers
    reactive programming
    【转】iOS中流(Stream)的使用
    NSStream 流式思想
    Future模式 总结
    备忘
  • 原文地址:https://www.cnblogs.com/a1b3c7d9/p/11408652.html
Copyright © 2011-2022 走看看