求方差的套路见过了,所以直接A掉了hh
答案表示一下就是
∑i=1m(x¯−xi)2m∗m2
,其中
x¯=∑i=1mxim
,记前缀和sum,则sum[n]显然就是
∑i=1mxi
,化简后得
ans=m∗∑i=1mxi2−sum[n]2
所以原问题转化为求把n个数分成m部分,使得每部分的和的平方和最小。f[i][k]表示把前i个数分成k部分的最小值。则
f[i][k]=min{f[j][k−1]+(sum[i]−sum[j])2|k−1≤j<i}
复杂度
O(n3)
过不去,进行斜率优化。设k1< k2且k1优于k2,则
f[k2][k−1]−f[k1][k−1]+sum[k2]2−sum[k1]22∗(sum[k2]−sum[k1])>sum[i]
,因此我们维护一个下凸曲线,保证队列中的斜率单增。用滚动数组再优化一下空间复杂度为O(n),时间复杂度为
O(n2)
#include <cstdio>
#include <cstring>
#define N 3010
#define ll long long
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
int n,m,a[N],q[N],h=0,t=1,p=0;
ll sum[N],f[2][N];
inline double slope(int k1,int k2){
return (f[p][k2]-f[p][k1]+sum[k2]*sum[k2]-sum[k1]*sum[k1])*0.5/(sum[k2]-sum[k1]);
}
int main(){
// freopen("a.in","r",stdin);
n=read();m=read();int x=0;
for(int i=1;i<=n;++i) sum[i]=read(),sum[i]+=sum[i-1];
for(int i=1;i<=n;++i) f[p][i]=sum[i]*sum[i];
for(int k=2;k<=m;++k){
h=0;t=1;q[++h]=k-1;
for(int i=k;i<=n;++i){
while(h<t&&slope(q[h],q[h+1])<sum[i]) ++h;
f[p^1][i]=f[p][q[h]]+(sum[i]-sum[q[h]])*(sum[i]-sum[q[h]]);
while(h<t&&slope(q[t],i)<slope(q[t-1],q[t])) --t;
q[++t]=i;
}p^=1;
}
printf("%lld\n",f[p][n]*m-sum[n]*sum[n]);
return 0;
}