洛谷传送门
BZOJ传送门
题目描述
Pine开始了从 S S S地到 T T T地的征途。
从 S S S地到 T T T地的路可以划分成 n n n段,相邻两段路的分界点设有休息站。
Pine计划用 m m m天到达 T T T地。除第 m m m天外,每一天晚上Pine都必须在休息站过夜。所以,一段路必须在同一天中走完。
Pine希望每一天走的路长度尽可能相近,所以他希望每一天走的路的长度的方差尽可能小。
帮助Pine求出最小方差是多少。
设方差是 v v v,可以证明, v × m 2 v\times m^2 v×m2是一个整数。为了避免精度误差,输出结果时输出 v × m 2 v\times m^2 v×m2。
输入输出格式
输入格式:
第一行两个数 n n n、 m m m。
第二行 n n n 个数,表示 n n n 段路的长度
输出格式:
一个数,最小方差乘以 m 2 m^2 m2 后的值
输入输出样例
输入样例#1:
5 2
1 2 5 8 6
输出样例#1:
36
说明
对于 30 % 30\% 30% 的数据, 1 ≤ n ≤ 10 1 \le n \le 10 1≤n≤10
对于 60 % 60\% 60% 的数据, 1 ≤ n ≤ 100 1 \le n \le 100 1≤n≤100
对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 3000 1 \le n \le 3000 1≤n≤3000
保证从 S S S 到 T T T 的总路程不超过 30000 30000 30000 。
解题分析
我们先一波化简, 发现答案是 ( ∑ i = 1 n x ) 2 + n ∑ i = 1 n x 2 (\sum_{i=1}^{n} x)^2+n\sum_{i=1}^{n}x^2 (∑i=1nx)2+n∑i=1nx2。前面那玩意是个常数, 后面我们也只需要求出 ∑ i = 1 n x 2 \sum_{i=1}^{n}x^2 ∑i=1nx2的最小值就好了。
设
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示我们已经走了
i
i
i天, 走了
j
j
j段路,
s
u
m
[
j
]
sum[j]
sum[j]为
j
j
j段路的长度和,那么设当前的最优解为第
i
−
1
i-1
i−1天走了
k
k
k段路 ,我们尝试从第
u
u
u段路
(
u
>
k
)
(u>k)
(u>k)去转移 ,如果更优的话那么就有:
d
p
[
i
−
1
]
[
k
]
+
(
s
u
m
[
j
]
−
s
u
m
[
k
]
)
2
>
d
p
[
i
−
1
]
[
u
]
+
(
s
u
m
[
j
]
−
s
u
m
[
u
]
)
2
d
p
[
i
−
1
]
[
k
]
−
2
∗
s
u
m
[
j
]
∗
s
u
m
[
k
]
+
s
u
m
[
k
]
2
>
d
p
[
i
−
1
]
[
u
]
−
2
∗
s
u
m
[
j
]
∗
s
u
m
[
u
]
+
s
u
m
[
u
]
2
dp[i-1][k]+(sum[j]-sum[k])^2>dp[i-1][u]+(sum[j]-sum[u])^2 \\dp[i-1][k]-2*sum[j]*sum[k]+sum[k]^2>dp[i-1][u]-2*sum[j]*sum[u]+sum[u]^2
dp[i−1][k]+(sum[j]−sum[k])2>dp[i−1][u]+(sum[j]−sum[u])2dp[i−1][k]−2∗sum[j]∗sum[k]+sum[k]2>dp[i−1][u]−2∗sum[j]∗sum[u]+sum[u]2
我们发现有一部分只与
j
j
j有关, 提出来:
d
p
[
i
−
1
]
[
u
]
−
d
p
[
i
−
1
]
[
k
]
+
s
u
m
[
u
]
2
−
s
u
m
[
k
]
2
s
u
m
[
u
]
−
s
u
m
[
k
]
<
2
∗
s
u
m
[
j
]
\frac{dp[i-1][u]-dp[i-1][k]+sum[u]^2-sum[k]^2}{sum[u]-sum[k]}<2*sum[j]
sum[u]−sum[k]dp[i−1][u]−dp[i−1][k]+sum[u]2−sum[k]2<2∗sum[j]
发现右边左边都是单增的, 那么就可以一波斜率优化了。
听说可以带权二分搞到
O
(
n
l
o
g
(
∑
x
)
)
O(nlog(\sum x))
O(nlog(∑x))?蒟蒻不会, 留坑…
代码如下:
#include <cstdio>
#include <cstring>
#include <cctype>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define db double
#define MX 3050
#define ll long long
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
for (; !isdigit(c); c = gc);
for (; isdigit(c); c = gc)
x = (x << 1) + (x << 3) + c - 48;
}
template <class T> IN T sqr (const T &x) {return x * x;}
ll dp[MX][MX], sum[MX];
int que[MX];
int tot, seg, head, tail;
IN db slope(R int cur, R int now, R int tar) {return 1.0 * (dp[cur][now] - dp[cur][tar] + sqr(sum[now]) - sqr(sum[tar])) / (sum[now] - sum[tar]);}
int main(void)
{
int buf, best;
in(tot), in(seg);
std::memset(dp, 63, sizeof(dp)); dp[0][0] = 0;
for (R int i = 1; i <= tot; ++i) in(buf), sum[i] = sum[i - 1] + buf;
for (R int i = 1; i <= tot; ++i) dp[1][i] = sqr(sum[i]);
for (R int i = 2; i <= seg; ++i)
{
head = 1, tail = 0;
std::memset(que, 0, sizeof(que));
for (R int j = 1; j <= tot; ++j)
{
W (head < tail && slope(i - 1, que[head], que[head + 1]) <= 2 * sum[j]) ++head;
best = que[head];
dp[i][j] = dp[i - 1][best] + sqr(sum[j] - sum[best]);
W (head < tail && slope(i - 1, que[tail - 1], que[tail]) > slope(i - 1, que[tail], j)) --tail;
que[++tail] = j;
}
}
printf("%lld", 1ll * seg * dp[seg][tot] - sqr(sum[tot]));
}