bzoj3675 [Apio2014]序列分割
Posted 逢山开路 遇水架桥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj3675 [Apio2014]序列分割相关的知识,希望对你有一定的参考价值。
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3675
【题解】
当时想的时候猜了下从前往后分比较优。
后来证明了一下怎么分都一样。。可以把贡献式子拆开来分析。
这样分析完就可以得到贡献=Σ每两个块的和的积
那么我们整理可以得到贡献=Σ第i个块的和*(前面所有块的和)
那么有dp: f[i,j]表示到第i块,选择的数是a[1]...a[j]的贡献max。
那么直接dp有50分。
# include <stdio.h> # include <string.h> # include <algorithm> // # include <bits/stdc++.h> using namespace std; typedef long long ll; typedef long double ld; typedef unsigned long long ull; const int M = 1e5 + 10, N = 210; const int mod = 1e9+7; # define RG register # define ST static int n, K, a[M]; ll s[M], f[N][M]; int pre[N][M]; inline void prt(int i, int j) { if(i == 1) return; prt(i-1, pre[i][j]); printf("%d ", pre[i][j]); } int main() { scanf("%d%d", &n, &K); for (int i=1; i<=n; ++i) scanf("%d", &a[i]), s[i] = s[i-1] + a[i]; for (int i=2; i<=K+1; ++i) for (int j=1; j<=n; ++j) { f[i][j] = -1e18; for (int k=1; k<j; ++k) { if(f[i][j] < f[i-1][k] + (s[j]-s[k])*s[k]) { f[i][j] = f[i-1][k] + (s[j]-s[k])*s[k]; pre[i][j] = k; } } } printf("%lld\n", f[K+1][n]); prt(K+1, n); return 0; }
肯定不能直接dp,需要优化
明显可斜率优化。
直接上即可。
uoj版本(输出方案,空间不限制)
# include <stdio.h> # include <string.h> # include <algorithm> // # include <bits/stdc++.h> using namespace std; typedef long long ll; typedef long double ld; typedef unsigned long long ull; const int M = 1e5 + 10, N = 210; const int mod = 1e9+7; # define RG register # define ST static int n, K, a[M]; ll s[M], f[N][M]; int pre[N][M]; int q[M], head, tail; inline void prt(int i, int j) { if(i == 1) return; prt(i-1, pre[i][j]); printf("%d ", pre[i][j]); } inline ll slop(int i, int k1, int k2) { return f[i-1][k1]-f[i-1][k2]+s[k2]*s[k2]-s[k1]*s[k1]; } inline ll getdp(int i, int j, int x) { return f[i-1][x]+(s[j]-s[x])*s[x]; } int main() { scanf("%d%d", &n, &K); for (int i=1; i<=n; ++i) scanf("%d", &a[i]), s[i] = s[i-1] + a[i]; for (int i=2; i<=K+1; ++i) { head = 1, tail = 0; for (int j=1; j<=n; ++j) { while(head<tail && slop(i, q[head], q[head+1]) <= (s[q[head+1]] - s[q[head]]) * s[j]) ++head; f[i][j] = getdp(i, j, q[head]); pre[i][j] = q[head]; while(head<tail && slop(i, q[tail-1], q[tail]) * (s[j] - s[q[tail]]) >= slop(i, q[tail], j) * (s[q[tail]] - s[q[tail-1]])) --tail; q[++tail] = j; } } printf("%lld\n", f[K+1][n]); prt(K+1, n); return 0; }
bzoj版本(不输出方案,空间限制)
# include <stdio.h> # include <string.h> # include <algorithm> // # include <bits/stdc++.h> using namespace std; typedef long long ll; typedef long double ld; typedef unsigned long long ull; const int M = 1e5 + 10, N = 210; const int mod = 1e9+7; # define RG register # define ST static int n, K, a[M]; ll s[M], f[2][M]; int q[M], head, tail; int pre, cur; inline ll slop(int k1, int k2) { return f[pre][k1]-f[pre][k2]+s[k2]*s[k2]-s[k1]*s[k1]; } inline ll getdp(int j, int x) { return f[pre][x]+(s[j]-s[x])*s[x]; } int main() { scanf("%d%d", &n, &K); for (int i=1; i<=n; ++i) scanf("%d", &a[i]), s[i] = s[i-1] + a[i]; pre = 0, cur = 1; for (int i=2; i<=K+1; ++i) { head = 1, tail = 0; for (int j=1; j<=n; ++j) { while(head<tail && slop(q[head], q[head+1]) <= (s[q[head+1]] - s[q[head]]) * s[j]) ++head; f[cur][j] = getdp(j, q[head]); while(head<tail && slop(q[tail-1], q[tail]) * (s[j] - s[q[tail]]) >= slop(q[tail], j) * (s[q[tail]] - s[q[tail-1]])) --tail; q[++tail] = j; } swap(pre, cur); } printf("%lld\n", f[pre][n]); return 0; }
以上是关于bzoj3675 [Apio2014]序列分割的主要内容,如果未能解决你的问题,请参考以下文章
bzoj 3675 [Apio2014]序列分割(斜率DP)