ZR W14 Scapegoat

题意:给你n个数,每个数分任意份,最后n个数总共分成m份,最优方案下这m个数的方差是多少,T组数据(T<=100,1<=N<=M<=2*1e5,∑M<=3*1e6)

sol

首先把方差公式化柿子,设分成m份后每个数为xi,平均数为xr,m个数的和为sum。可以观察到,无论怎么分,xr和sum是不变的。

1/m*∑i=1-m(xi-xr)2=1/m*(i=1-m(xi2+xr2-2*xi*xr))=1/m*(m*xr2-2*sum*xr+∑i=1-mxi2)=xr2-2*xr*xr+1/m*i=1-mxi2

=-xr2+1/m*i=1-mxi2

xr和m都是常数,所以只要最小化红色部分就可以了

设初始n个数权值为ai,最后被分成bi份,红色部分即为∑i=1-nbi*(ai/bi)2=i=1-nai2/bi

最后贪心,就是把初始的n个数看成n组,显然地是为了使方差小每组应该等分,所以枚举m-n次,在判断每次应该使哪个组多加一份时

找ai2/(bi+1)-ai2/bi(这是这次操作带来的收益,显然是负数)最大的一组,这个可以用个数据结构维护

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define D double
using namespace std;
int A[200100];
struct Node{
    D v;
    int i,bi;
    Node(){}
    Node(D V,int Bi,int num){
        v=V; bi=Bi; i=num;
    }
    bool operator < (const Node &A)const{
        D v1=v/((D)bi+1)-(v/(D)bi),v2=A.v/((D)A.bi+1)-A.v/(D)A.bi;
        if (v1!=v2) return v1>v2;
        return i<A.i;
    }
};
priority_queue <Node> Q;
int main(){
    int T; cin>>T; 
    for (int hh=1;hh<=T;hh++){
        printf("Case #%d: ",hh);
        int i,n,m; scanf("%d %d",&n,&m);
        D xr,sum=0,res=0;
        for (i=1;i<=n;i++) scanf("%d",&A[i]),sum+=(D)A[i];
        xr=sum/(D)m;
        res=-xr*xr;
        D a=0;
        for (i=1;i<=n;i++) 
            Q.push(Node((D)A[i]*(D)A[i],1,i));
        for (i=n+1;i<=m;i++){
            Node x=Q.top(); Q.pop();
            x.bi++;
            Q.push(x);
        }
        while (!Q.empty()){
            Node x=Q.top(); Q.pop();
            a+=x.v/(D)x.bi;
        }
        res+=a/(D)m;
        printf("%0.12lf\n",res);
    }
}

猜你喜欢

转载自www.cnblogs.com/Pedestrian6/p/9196205.html