BZOJ4518 [Sdoi2016]征途
[传送门]
设最后 $m$ 段每段数字之和为 $a_i$,那么最后答案为 $m^2 \sum \dfrac {\left( a_{i}-\dfrac {s}{m}\right) ^{2}}{m}$
把括号拆开后最后答案为 $m\sum a_i ^{2}-S^{2}$
要求最小化$\sum a_i ^{2}$,那么直接上斜率优化
$dp[i][k]$ 表示在 $i$ 处做第 $k$ 次休息。
那么 $dp[i][k] = min(dp[j][k - 1]) + (sum[i] - sum[j])^2$
$dp[i][k] + 2 \times sum[i] \times sum[j] = dp[j][k - 1] + sum[j] ^ 2 + sum[i] ^ 2$
斜率为 $2 \times sum[i]$,自变量为 $sum[j]$,截距为 $dp[i][k]$,因变量为 $dp[j][k - 1] + sum[j] ^ 2 + sum[i] ^ 2$。
求截距最小值即可。由于斜率单调,由决策单调性可 $O(1)$ 得到最优决策点。
时间复杂度 $O(nm)$
边界为 $dp[i][0] = sum[i] ^ 2$
#include <bits/stdc++.h> #define ll long long using namespace std; namespace IO { const int MAXSIZE = 1 << 20; char buf[MAXSIZE], *p1, *p2; #define gc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, MAXSIZE, stdin), p1 == p2) ? EOF : *p1++) template<typename T> inline void read(T &x) { x = 0; T f = 1; char ch = gc(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = gc(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = gc(); } x *= f; } } using namespace IO; const int N = 3e3 + 7; const double eps = 1e-10; ll dp[N][2], s[N]; int n, m, que[N], l, r; inline ll sqr(int x) { return x * x; } inline ll X(int i) { return s[i]; } inline ll Y(int i, int cur) { return dp[i][cur] + sqr(s[i]); } inline ll K(int i, int j, int cur) { //if (X(i) == X(j)) return 0; return 1.0 * (Y(i, cur) - Y(j, cur)) / (X(i) - X(j)); } inline int dcmp(double x) { if (fabs(x) < eps) return 0; return x < 0 ? -1 : 1; } int main() { // freopen("in.txt", "r", stdin); read(n), read(m); for (int i = 1; i <= n; i++) read(s[i]), s[i] += s[i - 1], dp[i][0] = sqr(s[i]); for (int k = 1; k <= m - 1; k++) { int cur = k & 1; que[l = r = 1] = k; for (int i = k + 1; i <= n; i++) { while (l < r && dcmp(K(que[l], que[l + 1], cur ^ 1) - 2 * s[i]) < 0) l++; int j = que[l]; dp[i][cur] = dp[j][cur ^ 1] + sqr(s[i] - s[j]); while (l < r && dcmp(K(que[r], que[r - 1], cur ^ 1) - K(que[r], i, cur ^ 1)) > 0) r--; que[++r] = i; } } printf("%lld\n", m * dp[n][(m - 1) & 1] - sqr(s[n])); return 0; }