题意:有 n n n个数,要把这些数分成连续的 m m m段使方差最小
由于 s 2 = ∑ i = 1 n ( d − d i ) 2 m s^2=\frac{\sum_{i=1}^{n}(d-d_i)^2}{m} s2=m∑i=1n(d−di)2
则 s 2 ∗ m 2 = m ∗ ∑ i = 1 n ( d − d i ) 2 s^2*m^2=m*\sum_{i=1}^{n}(d-d_i)^2 s2∗m2=m∗∑i=1n(d−di)2
令 s u m k = ∑ i = 1 k d i sum_k=\sum_{i=1}^{k}d_i sumk=∑i=1kdi
因为
d
=
∑
i
=
1
n
d
i
m
=
s
u
m
n
m
d=\frac{\sum_{i=1}^{n}d_i}{m}=\frac{sum_n}{m}
d=m∑i=1ndi=msumn
化简得
∑
i
=
1
n
(
d
i
2
)
−
(
∑
i
=
1
n
d
i
)
2
\sum_{i=1}^{n}(d_i^2)-(\sum_{i=1}^{n}d_i)^2
∑i=1n(di2)−(∑i=1ndi)2
也就是要最小化 ∑ i = 1 n ( d i 2 ) \sum_{i=1}^{n}(d_i^2) ∑i=1n(di2)
那就有一个很显然的
O
(
n
2
m
)
O(n^2m)
O(n2m)的
d
p
dp
dp
f
[
j
]
[
i
]
f[j][i]
f[j][i]表示前
i
i
i个分
j
j
j段的最小值
f [ k ] [ i ] = M i n ( f [ k − 1 ] [ j ] + ( s u m i − s u m j ) 2 ) f[k][i]=Min(f[k-1][j]+(sum_i-sum_j)^2) f[k][i]=Min(f[k−1][j]+(sumi−sumj)2)
考虑优化
如果一个决策点
a
a
a比
b
b
b优
则
f
[
k
]
[
a
]
+
(
s
u
m
i
−
s
u
m
a
)
2
<
f
[
k
]
[
b
]
+
(
s
u
m
i
−
s
u
m
b
)
2
f[k][a]+(sum_i-sum_a)^2<f[k][b]+(sum_i-sum_b)^2
f[k][a]+(sumi−suma)2<f[k][b]+(sumi−sumb)2
化简就变成了 f a + s u m a 2 − f b − s u m b 2 s u m a − s u m b ≤ 2 ∗ s u m i \frac{f_a+sum_a^2-f_b-sum_b^2}{sum_a-sum_b}\le2*sum_i suma−sumbfa+suma2−fb−sumb2≤2∗sumi
把
f
a
+
s
u
m
a
2
f_a+sum_a^2
fa+suma2看做
x
x
x,
s
u
m
a
sum_a
suma看做
y
y
y
因为
s
u
m
sum
sum单调
也就是说可以维护一个斜率递增的序列
答案也就从当前最小的斜率转移过来就行了
#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;
}