传送门
把式子展开后发现就是要求:
m∗(∑mi=1sum′[i])−sum[n]2
m
∗
(
∑
i
=
1
m
s
u
m
′
[
i
]
)
−
s
u
m
[
n
]
2
的最小值。
于是只需要求:
m∗(∑mi=1sum′[i])
m
∗
(
∑
i
=
1
m
s
u
m
′
[
i
]
)
的最小值。
于是设
f[i][j]
f
[
i
]
[
j
]
表示前i个分了j组的最小值。
显然有:
f[i][j]=min(f[k][j−1]+(sum[i]−sum[k])2)
f
[
i
]
[
j
]
=
m
i
n
(
f
[
k
]
[
j
−
1
]
+
(
s
u
m
[
i
]
−
s
u
m
[
k
]
)
2
)
<=>
f[i][j]=min(f[k][j−1]+sum[k]2−2sum[i]∗sum[k])+sum[i]2
f
[
i
]
[
j
]
=
m
i
n
(
f
[
k
]
[
j
−
1
]
+
s
u
m
[
k
]
2
−
2
s
u
m
[
i
]
∗
s
u
m
[
k
]
)
+
s
u
m
[
i
]
2
对于两个决策
k1<k2
k
1
<
k
2
且k2比k1更优,有:
f[k1][j−1]+sum[k1]2−2sum[i]∗sum[k1]
f
[
k
1
]
[
j
−
1
]
+
s
u
m
[
k
1
]
2
−
2
s
u
m
[
i
]
∗
s
u
m
[
k
1
]
>
f[k2][j−1]+sum[k2]2−2sum[i]∗sum[k2]
f
[
k
2
]
[
j
−
1
]
+
s
u
m
[
k
2
]
2
−
2
s
u
m
[
i
]
∗
s
u
m
[
k
2
]
令
t[k]=f[k][j−1]+sum[k]2
t
[
k
]
=
f
[
k
]
[
j
−
1
]
+
s
u
m
[
k
]
2
=>
(t[k1]−t[k2])/(sum[k1]−sum[k2])<2sum[i]
(
t
[
k
1
]
−
t
[
k
2
]
)
/
(
s
u
m
[
k
1
]
−
s
u
m
[
k
2
]
)
<
2
s
u
m
[
i
]
果断斜率优化。
代码:
#include<bits/stdc++.h>
#define ll long long
#define N 3005
using namespace std;
inline ll read(){
ll ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
int n,m,hd,tl,q[N];
ll sum[N],f[N][N];
inline double slope(int k,int i,int j){return 1.0*(f[i][k]+sum[i]*sum[i]-f[j][k]-sum[j]*sum[j])/(sum[i]-sum[j]);}
int main(){
n=read(),m=read();
for(int i=1;i<=n;++i)sum[i]=sum[i-1]+read(),f[i][0]=1e18;
for(int j=1;j<=m;++j){
hd=tl=1,q[1]=0;
for(int i=1;i<=n;++i){
while(hd<tl&&slope(j-1,q[hd+1],q[hd])<2.0*sum[i])++hd;
int k=q[hd];
f[i][j]=f[k][j-1]+(sum[i]-sum[k])*(sum[i]-sum[k]);
while(hd<tl&&slope(j-1,q[tl],q[tl-1])>slope(j-1,i,q[tl]))--tl;
q[++tl]=i;
}
}
cout<<(m*f[n][m]-sum[n]*sum[n]);
return 0;
}