【BZOJ4518】【SDOI2016】—征途(斜率优化dp)
题意:有个数,要把这些数分成连续的段使方差最小
由于
则
令
因为
化简得
也就是要最小化
那就有一个很显然的的
表示前个分段的最小值
考虑优化
如果一个决策点比优
则
化简就变成了
把看做,看做
因为单调
也就是说可以维护一个斜率递增的序列
答案也就从当前最小的斜率转移过来就行了
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){
char ch=getchar();
int res=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=getchar();
return res*f;
}
const int N=3005;
const double eps=1e-9;
int sum[N],f[N][N],d[N],n,m;
double q[N];
inline int P(int x){
return x*x;
}
inline double slope(int p,int a,int b){
return (double)(1.0*f[p][a]+P(sum[a])-f[p][b]-P(sum[b]))/(sum[a]-sum[b]);
}
signed main(){
n=read(),m=read();
for(int i=1;i<=n;i++)d[i]=read(),sum[i]=sum[i-1]+d[i],f[1][i]=P(sum[i]);
for(int j=2;j<=m;j++){
int hd=1,tl=0;
for(int i=1;i<=n;i++){
while(hd<tl&&slope(j-1,q[hd],q[hd+1])<2*sum[i])hd++;
int now=q[hd];
f[j][i]=f[j-1][now]+P(sum[i]-sum[now]);
while(hd<tl&&slope(j-1,q[tl-1],q[tl])>slope(j-1,q[tl-1],i))tl--;
q[++tl]=i;
}
}
int ans=m*f[m][n]-P(sum[n]);
cout<<ans;
}