# 题解 P3648 [APIO2014]序列分割

$f_{i, j}$ 表示前 $j$ 个分成 $i$ 段的答案。

$f_{i, j} = \max \{f_{i-1, k} + (sum_j - sum_k) \times sum_k\}$

\boxed{ \begin{aligned} \text{排版}&\text{精美的伪代码 : 决策单调性二分}\\ \hline 1.\ & \text{solve}(l,r,L,R) :\\ 2.\ & \kern{2em}\text{if}(l>r) \ \text{exit} \\ 3.\ & \kern{2em}mid \leftarrow (l+r) / 2 \\ 4.\ & \kern{2em}pos \leftarrow \text{query}[L,R,mid] \\ 5.\ & \kern{2em}f_{mid} \leftarrow g(pos) \\ 6.\ & \kern{2em} \text{solve}(l,mid-1,L,pos), \text{solve}(mid+1,r,pos,R) \end{aligned} }

#include <cstdio>
#include <algorithm>
#define int long long
const int N = 100005;
int n, m, sum[N], f[205][N], ans[N], pre[205][N];
void solve(int l, int r, int L, int R, int k) {
if (l > r) return;
int mid = l + (r-l) / 2;
int pos = 0, max = 0;
for (int i = L; i <= R; i++) {
int an = f[k-1][i] + (sum[mid] - sum[i]) * sum[i];
if (an > max) max = an, pos = i;
}
f[k][mid] = max; pre[k][mid] = pos;
solve(l, mid-1, L, pos, k), solve(mid+1, r, pos, R, k);
}
signed main() {
scanf("%lld%lld", &n, &m);
for (int i = 1; i <= n; i++) scanf("%lld", &sum[i]), sum[i] += sum[i-1];
for (int i = 1; i <= m; i++) solve(1, n, 1, n, i);
for (int now = n, k = m; now; now = pre[k][now], k--) ans[++ans[0]] = now;
std::reverse(ans+1, ans+1+ans[0]);
printf("%lld\n", f[m][n]);
for (int i = 1; i < ans[0]; i++) printf("%lld ", ans[i]);
return 0;
}

posted @ 2021-07-20 15:51  Acfboy  阅读(39)  评论(0编辑  收藏  举报