[APIO 2014] 序列分割
作者:互联网
[题目链接]
https://www.lydsy.com/JudgeOnline/problem.php?id=3675
[算法]
首先 , 我们发现将一段序列切成若干段所获得的收益与顺序无关
于是我们可以用fi,j表示切i次 , 前j个数的最大收益
令sumi表示ai的前缀和
显然 , fi,j = max{ fi-1,k + sumk * (sumj - sumk) }
斜率优化即可
此题内存限制较紧 , 可以使用滚动数组优化空间复杂度
时间复杂度 : O(NK)
[代码]
#include<bits/stdc++.h> using namespace std; #define MAXN 100010 #define MAXK 210 typedef long long ll; typedef long double ld; typedef unsigned long long ull; int n , k , l , r; ll X[MAXN] , Y[MAXN] , sum[MAXN] , f[2][MAXN]; int a[MAXN] , q[MAXN] , last[MAXK][MAXN]; template <typename T> inline void chkmax(T &x,T y) { x = max(x,y); } template <typename T> inline void chkmin(T &x,T y) { x = min(x,y); } template <typename T> inline void read(T &x) { T f = 1; x = 0; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0'; x *= f; } int main() { read(n); read(k); for (int i = 1; i <= n; i++) { read(a[i]); sum[i] = sum[i - 1] + a[i]; } for (int i = 1; i <= k; i++) { int now = i & 1 , pre = now ^ 1; f[now][q[l = r = 1] = 0] = 0; for (int j = 1; j <= n; j++) { while (l < r && Y[q[l + 1]] - Y[q[l]] >= -sum[j] * (X[q[l + 1]] - X[q[l]])) ++l; f[now][j] = Y[q[l]] + X[q[l]] * sum[j]; X[j] = sum[j]; Y[j] = f[pre][j] - sum[j] * sum[j]; last[i][j] = q[l]; while (l < r && (Y[j] - Y[q[r]]) * (X[q[r]] - X[q[r - 1]]) >= (Y[q[r]] - Y[q[r - 1]]) * (X[j] - X[q[r]])) --r; q[++r] = j; } } printf("%lld\n" , f[k & 1][n]); int now = n , s = k; vector< int > ans; while (now > 0) { now = last[s][now]; if (now) ans.push_back(now); --s; } reverse(ans.begin() , ans.end()); // 输出方案... return 0; }
标签:APIO,int,sum,long,MAXN,ans,序列,2014,now 来源: https://www.cnblogs.com/evenbao/p/10354216.html