虽然又是一遍AC的,但不得不说这题打得我好慌张,还调了十几分钟,虽然都是智障错误。
回归正题。猛然一看,n只有3000,貌似不要斜率优化耶。
但事实上,普通DP是N^2*M的,所以还是得斜率优化哈哈。
构造数列A1~Am,表示第i段的和。
方差乘m^2后长这样子:m*Ai^2-(Ai)^2
惊喜的发现(Ai)^2是个定值,那么我们只要使Ai^2最小即可。
就问你眼不眼熟?这不就是例一吗?但是此题多一维。
可以推导出状转方程:f[i][j]=min{f[k][j-1]+(sum[i]-sum[k])^2}
发现f[……][i]只跟f[……][i-1]有关,那就队列里放i-1的然后统计i的答案呗。
看代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=3000+10;
int n,m,f[maxn][maxn],sum[maxn],d[maxn],q[maxn];
int yval(int a,int b,int c){
return f[b][c]+sum[b]*sum[b]-f[a][c]-sum[a]*sum[a];
}
int xval(int a,int b){return sum[b]-sum[a];}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
scanf("%lld",&d[i]);
sum[i]=sum[i-1]+d[i];
}
//memset(f,0x3f,sizeof(f));
for(int i=1;i<=n;i++)
f[i][1]=sum[i]*sum[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&&yval(q[l],q[l+1],i-1)<=xval(q[l],q[l+1])*2*sum[j])l++;
f[j][i]=f[q[l]][i-1]+(sum[j]-sum[q[l]])*(sum[j]-sum[q[l]]);
//printf("%d %d %d %d %d
",j,i,q[l],f[q[l]][i-1],f[j][i]);
while(l<r&&yval(q[r-1],q[r],i-1)*xval(q[r],j)>=xval(q[r-1],q[r])*yval(q[r],j,i-1))r--;
q[++r]=j;
//printf("%d %d
",l,r);
}
}
//for(int i=1;i<=m;i++)
// for(int j=1;j<=n;j++)
// printf("f[%d][%d]=%d
",j,i,f[j][i]);
printf("%lld
",m*f[n][m]-sum[n]*sum[n]);
return 0;
}
看到那一堆调试代码了吗?警醒读者:别把i和j弄反了!!!