题意
把 个数分成 段,要求每段和的方差尽可能小,求最小值。
。
题解
我这个辣鸡的错误解法
方程还是比较容易想到的,用 表示当前是第 段,分到第 个数的最优解,方程为:
然后我就硬着头皮继续化简。因为 后把每一项拆出来还是可能出现非整数,所以我就再 ,最后除掉就行了。
看起来有 和 相乘的项,所以往斜率优化去化简。用 代表 的前缀和,最后我写了两张草稿纸后得到了个又长又丑的式子:
然后我悲催的发现 虽然满足单调性,但可能是负的,不能用斜率优化, 。
正解
表示方差, 表示每一段的和,则
可以发现 是固定的,所以最小化 就行了。直接上方程:
斜率 递增。
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
char ch=getchar();
int f=1,x=0;
while (ch<'0' || ch>'9')
{
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0' && ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
int n,m,p,sum,s[3005],f[3005][3005],q[3005];
signed main()
{
n=read(); m=read();
for (int i=1;i<=n;i++) s[i]=s[i-1]+read();
for (int i=1;i<=n;i++) f[1][i]=s[i]*s[i];
for (int i=2;i<=m;i++)
{
int l=1,r=1; q[l]=0;
for (int j=1;j<=n;j++)
{
while (l<r && f[i-1][q[l+1]]+s[q[l+1]]*s[q[l+1]]-f[i-1][q[l]]-s[q[l]]*s[q[l]]
<=(s[q[l+1]]-s[q[l]])*2*s[j]) l++;
f[i][j]=f[i-1][q[l]]+(s[j]-s[q[l]])*(s[j]-s[q[l]]);
while (l<r && (f[i-1][q[r]]+s[q[r]]*s[q[r]]-f[i-1][q[r-1]]-s[q[r-1]]*s[q[r-1]])*(s[j]-s[q[r]])
>=((f[i-1][j]+s[j]*s[j]-f[i-1][q[r]])-s[q[r]]*s[q[r]])*(s[q[r]]-s[q[r-1]])) r--;
q[++r]=j;
}
}
return !printf("%d",m*f[m][n]-s[n]*s[n]);
}