征途

虽然又是一遍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弄反了!!!

原文地址:https://www.cnblogs.com/syzf2222/p/12386827.html