题意:给你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); } }