Description
Pine 开始了从 S 地到 T 地的征途。
从 S 地到 T 地的路可以划分成 n 段,相邻两段路的分界点设有休息站。
Pine 计划用 m 天到达 T 地。除第 m 天外,每一天晚上 Pine 都必须在休息站过夜。所以,一段路必须在同一天中走完。
Pine 希望每一天走的路长度尽可能相近,所以他希望每一天走的路的长度的方差尽可能小。
帮助 Pine 求出最小方差是多少。
设方差是 v,可以证明,vm^2 是一个整数。为了避免精度误差,输出结果时输出 vm^2。
Input
第一行两个数 n、m。
第二行 n 个数,表示 n 段路的长度。
Output
一个数,最小方差乘以 m^2 后的值。
Sample Input
5 2
1 2 5 8 6
Sample Output
36
Data Constraint
对于 30% 的数据,1≤n≤10。
对于 60% 的数据,1≤n≤100。
对于 100% 的数据,1≤n≤3000。
保证从 S 到 T 的总路程不超过 30000。
分析:
化简式子得
v
∗
m
2
=
m
∗
∑
i
=
1
m
v
i
2
−
s
u
m
2
v*m^2=m*\sum_{i=1}^{m}v_i^2-sum^2
v∗m2=m∗∑i=1mvi2−sum2
关键求子段平方和最小值。
斜率优化非常套路,不过我忘记了前面状态看成点,当前状态看成直线的那种做法。
这里讨论把前面状态看成直线,当前状态看成点的方法。
显然
f
[
i
]
[
k
]
=
min
j
=
0
i
−
1
f
[
j
]
[
k
−
1
]
+
(
s
u
m
[
i
]
−
s
u
m
[
j
]
)
2
f[i][k]=\min_{j=0}^{i-1}f[j][k-1]+(sum[i]-sum[j])^2
f[i][k]=j=0mini−1f[j][k−1]+(sum[i]−sum[j])2
也就是
f
[
i
]
[
k
]
−
s
u
m
[
i
]
2
=
−
2
∗
s
u
m
[
j
]
∗
s
u
m
[
i
]
+
s
u
m
[
j
]
2
+
f
[
j
]
[
k
−
1
]
f[i][k]-sum[i]^2=-2*sum[j]*sum[i]+sum[j]^2+f[j][k-1]
f[i][k]−sum[i]2=−2∗sum[j]∗sum[i]+sum[j]2+f[j][k−1]
显然
k
=
−
2
∗
s
u
m
[
j
]
k=-2*sum[j]
k=−2∗sum[j],
b
=
s
u
m
[
j
]
2
+
f
[
j
]
[
k
−
1
]
b=sum[j]^2+f[j][k-1]
b=sum[j]2+f[j][k−1]
因为斜率递减,而自变量递增,那么一旦一条直线比后面的直线不优,那么这条直线可以直接弹掉。
如果一条直线被它左右两条直线所完全覆盖,那么这条直线也没有用。可以通过求左右两条直线的交点是否比当前直线优。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#define LL long long
const int maxn=3e3+7;
using namespace std;
int n,m,h,t;
LL sum[maxn],f[maxn][maxn];
struct node{
LL k,b;
}q[maxn];
LL get(int p,LL x)
{
return q[p].k*x+q[p].b;
}
bool check(int x,int y,int z)
{
LL w1=(LL)(q[z].k-q[y].k)*(q[z].b-q[x].b);
LL w2=(LL)(q[x].k-q[z].k)*(q[y].b-q[z].b);
return w1<=w2;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%lld",&sum[i]);
for (int i=1;i<=n;i++) sum[i]+=sum[i-1];
for (int j=0;j<=n;j++) f[j][1]=sum[j]*sum[j];
for (int i=2;i<=m;i++)
{
h=1,t=0;
for (int j=0;j<=n;j++)
{
if (h<=t)
{
while ((h<t) && (get(h,sum[j])>=get(h+1,sum[j]))) h++;
f[j][i]=get(h,sum[j])+sum[j]*sum[j];
}
q[++t]=(node){-2*sum[j],f[j][i-1]+sum[j]*sum[j]};
while ((t-h>1) && (check(t-2,t-1,t)))
{
q[t-1]=q[t];
t--;
}
}
}
printf("%lld",f[n][m]*m-sum[n]*sum[n]);
}