背景:
h
e
h
e
.
.
.
hehe...
hehe...
题目传送门:
https://www.luogu.org/problemnew/show/P4072
题意:
n
n
n个数
a
i
a_i
ai,分成
m
m
m段,使方差(
v
v
v)尽可能小,求
v
∗
m
2
v*m^2
v∗m2。
思路:
推公式吧。
假设每一段的和为
x
1
,
x
2
,
.
.
.
,
x
m
x_1,x_2,...,x_m
x1,x2,...,xm。
设
s
u
m
k
=
∑
i
=
1
k
a
i
sum_k=\sum_{i=1}^ka_i
sumk=∑i=1kai。
另
x
=
∑
i
=
1
m
x
i
m
x=\frac{\sum_{i=1}^{m}x_i}{m}
x=m∑i=1mxi则有:
v
=
∑
i
=
1
m
(
x
i
−
x
)
2
m
v=\frac{\sum_{i=1}^{m}(x_i-x)^2}{m}
v=m∑i=1m(xi−x)2。
所以有:
v
=
∑
i
=
1
m
(
x
i
−
x
)
2
m
v=\frac{\sum_{i=1}^{m}(x_i-x)^2}{m}
v=m∑i=1m(xi−x)2
我们可以知道 x = ∑ i = 1 m x i m = ∑ i = 1 n a i m x=\frac{\sum_{i=1}^{m}x_i}{m}=\frac{\sum_{i=1}^na_i}{m} x=m∑i=1mxi=m∑i=1nai,所以有:
∑
i
=
1
m
(
x
i
−
x
)
2
m
\frac{\sum_{i=1}^{m}(x_i-x)^2}{m}
m∑i=1m(xi−x)2
=
∑
i
=
1
m
(
x
i
−
∑
i
=
1
n
a
i
m
)
2
m
=\frac{\sum_{i=1}^{m}(x_i- \frac{\sum_{i=1}^na_i}{m})^2}{m}
=m∑i=1m(xi−m∑i=1nai)2
=
∑
i
=
1
m
(
x
i
−
s
u
m
n
m
)
2
m
=\frac{\sum_{i=1}^{m}(x_i-\frac{sum_n}{m})^2}{m}
=m∑i=1m(xi−msumn)2
=
∑
i
=
1
m
(
x
i
2
−
2
x
i
s
u
m
n
m
+
s
u
m
n
2
m
2
)
m
=\frac{\sum_{i=1}^{m}(x_i^2-2x_i\frac{sum_n}{m}+\frac{sum_n^2}{m^2})}{m}
=m∑i=1m(xi2−2ximsumn+m2sumn2)
=
∑
i
=
1
m
(
x
i
2
)
−
∑
i
=
1
m
(
2
x
i
s
u
m
n
m
)
+
∑
i
=
1
m
(
s
u
m
n
2
m
2
)
)
m
=\frac{\sum_{i=1}^{m}(x_i^2)-\sum_{i=1}^{m}(2x_i\frac{sum_n}{m})+\sum_{i=1}^{m}(\frac{sum_n^2}{m^2}))}{m}
=m∑i=1m(xi2)−∑i=1m(2ximsumn)+∑i=1m(m2sumn2))
=
∑
i
=
1
m
x
i
2
m
−
∑
i
=
1
m
2
x
i
s
u
m
n
m
m
+
∑
i
=
1
m
s
u
m
n
2
m
2
m
=\frac{\sum_{i=1}^{m}x_i^2}{m}-\frac{\sum_{i=1}^{m}2x_i\frac{sum_n}{m}}{m}+\frac{\sum_{i=1}^{m}\frac{sum_n^2}{m^2}}{m}
=m∑i=1mxi2−m∑i=1m2ximsumn+m∑i=1mm2sumn2
=
∑
i
=
1
m
x
i
2
m
−
2
s
u
m
n
∑
i
=
1
m
x
i
m
2
+
∑
i
=
1
m
s
u
m
n
2
m
3
=\frac{\sum_{i=1}^{m}x_i^2}{m}-\frac{2sum_n\sum_{i=1}^{m}x_i}{m^2}+\frac{\sum_{i=1}^{m}sum_n^2}{m^3}
=m∑i=1mxi2−m22sumn∑i=1mxi+m3∑i=1msumn2
=
∑
i
=
1
m
x
i
2
m
−
2
s
u
m
n
2
m
2
+
m
∗
s
u
m
n
2
m
3
=\frac{\sum_{i=1}^{m}x_i^2}{m}-\frac{2sum_n^2}{m^2}+\frac{m*sum_n^2}{m^3}
=m∑i=1mxi2−m22sumn2+m3m∗sumn2
=
∑
i
=
1
m
x
i
2
m
−
2
s
u
m
n
2
m
2
+
s
u
m
n
2
m
2
=\frac{\sum_{i=1}^{m}x_i^2}{m}-\frac{2sum_n^2}{m^2}+\frac{sum_n^2}{m^2}
=m∑i=1mxi2−m22sumn2+m2sumn2
=
∑
i
=
1
m
x
i
2
m
−
s
u
m
n
2
m
2
=\frac{\sum_{i=1}^{m}x_i^2}{m}-\frac{sum_n^2}{m^2}
=m∑i=1mxi2−m2sumn2
由于只有
∑
i
=
1
m
x
i
2
\sum_{i=1}^{m}x_i^2
∑i=1mxi2是不定值,所以让其最小即可。
容易想到常规做法:设
f
i
,
l
f_{i,l}
fi,l表示前
i
i
i个数用
l
l
l次来选的最小值。
显然
f
i
,
l
=
min
j
=
1
i
−
1
f
j
,
l
−
1
+
(
s
u
m
i
−
s
u
m
j
)
2
f_{i,l}=\min_{j=1}^{i-1}f_{j,l-1}+(sum_i-sum_j)^2
fi,l=minj=1i−1fj,l−1+(sumi−sumj)2。
则斜率方程为:
f
j
,
l
−
1
−
f
k
,
l
−
1
+
s
u
m
j
2
−
s
u
m
k
2
s
u
m
j
−
s
u
m
k
<
2
s
u
m
i
\frac{f_{j,l-1}-f_{k,l-1}+sum_j^2-sum_k^2}{sum_j-sum_k}<2sum_i
sumj−sumkfj,l−1−fk,l−1+sumj2−sumk2<2sumi
最后套回去即可。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
int n,m;
LL a[3010],sum[3010],f[3010][3010];
int que[3010];
LL calc1(int x,int y)
{
return sum[y]-sum[x];
}
LL calc2(int x,int y,int z)
{
return (f[y][z-1]+sum[y]*sum[y])-(f[x][z-1]+sum[x]*sum[x]);
}
double calc(int x,int y,int z)
{
return (double)(calc2(x,y,z))/(double)(calc1(x,y));
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
sum[i]=sum[i-1]+a[i];
}
for(int i=1;i<=n;i++)
f[i][1]=sum[i]*sum[i];
for(int l=2;l<=m;l++)
{
int head=1,tail=1;
que[1]=0;
for(int i=1;i<=n;i++)
{
while(head<tail&&calc(que[head],que[head+1],l)<=(double)2*sum[i]) head++;
f[i][l]=f[que[head]][l-1]+calc1(que[head],i)*calc1(que[head],i);
while(head<tail&&calc(que[tail],i,l)<=calc(que[tail-1],que[tail],l)) tail--;
que[++tail]=i;
}
}
printf("%lld",f[n][m]*m-sum[n]*sum[n]);
}