题目链接
题解
用(a_i)表示没天走的路程,(s_i)表示(a_i)的前缀和
得到式子
(ans=m^2*{1over m}[sumlimits_{i=1}^m (a_i-{s_nover m})^2])
展开化简得到
(=m(sumlimits_{i=1}^ma_i^2+{s_n^2over m}-{2s_nsumlimits_{i=1}^ma_iover m}))
(=msumlimits_{i=1}^m{a_i}^2-{s_n}^2)
发现式子至于(a_i ^{2})有关
就是将n个数划分成m个部分然后让这些部分的平方和最小
令(dp(i,j))表示前i个数划分了j次的最小花费
那么枚举k (dp(i,j)=min{{dp(i-1,k)+{(s_j-s_k)}^2}})
把上面的式子展开得到
(dp(i,j)=min{-2s_ks_j+dp(i-1,k)+s_k^2+s_j^2})
然后就可以斜率优化惹
然后你可以滚掉一维 没用
代码
// luogu-judger-enable-o2
#include<cstdio>
#include<cstring>
#include<algorithm>
inline int read() {
int x = 0,f = 1;
char c = getchar ();
while(c < '0' || c > '9') { if(c == '-') f = -1;c = getchar();}
while(c <= '9' && c >= '0') x = x * 10 + c - '0' ,c = getchar();
return x * f;
}
const int maxn = 3007;
int n,m;
int a[maxn];
int dp[maxn][maxn];
int q[maxn];
double calc(int i,int j,int k) {
return ((1.0 * dp[i][j] + 1.0 * a[j] * a[j]) - (1.0 * dp[i][k] + 1.0 * a[k] * a[k])) / (1.0 * a[j] - a[k]);
}
int main() {
//freopen("journey.in","r",stdin);freopen("journey.out","w",stdout);
int n = read(),m = read();
for(int i=1;i<=n;i++)
a[i] += a[i-1] + read();
for(int i = 1;i <= n;++ i) dp[1][i] = a[i] * a[i];
for(int head,tail,i = 2;i <= m;i ++) {
head = 1, tail = 0;
for(int j = 1;j <= n;j ++) {
while(head < tail && calc(i - 1,q[head],q[head + 1 ]) < 2.0 * a[j]) head ++;
dp[i][j] = dp[i - 1][q[head]] + (a[j] - a[q[head]]) * (a[j]-a[q[head]]);
while(head < tail && calc(i - 1,q[tail],q[tail - 1]) > calc(i-1,q[tail],j) )tail --;
q[++tail]=j;
}
}
printf("%d
",m * dp[m][n] - a[n] * a[n]);
return 0;
}