bzoj3675[Apio2014]序列分割 斜率优化dp
Posted GXZlegend
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj3675[Apio2014]序列分割 斜率优化dp相关的知识,希望对你有一定的参考价值。
原文地址:http://www.cnblogs.com/GXZlegend/p/6835179.html
题目描述
输入
输入第一行包含两个整数n,k(k+1≤n)。
输出
输出第一行包含一个整数,为小H可以得到的最大分数。
样例输入
7 3
4 1 3 4 0 2 3
样例输出
108
题解
斜率优化dp
首先可以证得分割的顺序对答案没有影响。
设连续的3部分之和分别为a、b、c,则先分割ab再分割bc的分数为a*(b+c)+b*c=a*b+a*c+b*c,先分割bc再分割ab的分数为(a+b)*c+a*b=a*b+a*c+b*c,它们完全相同。
这样我们就可以从左到右按顺序依次分割。
设f[i][p]表示前i个数分割p次的最大分数,那么有f[i][p]=f[j][p-1]+(sum[i]-sum[j])*sum[j]
这样时间复杂度为O(n^2*p),会TLE,需要优化。
将dp方程变形,得sum[j]*sum[j]-f[j][p-1]=sum[i]*sum[j]-f[i][p],
这样可以斜率优化来解决,此时y=sum[j]*sum[j]-f[j][p-1],k=sum[i],x=sum[j],均为正数(其实是故意这样移项的)。
然后要求的是f[i][p]的最大值,即-f[i][p]的最小值,所以应该维护一个上凸包。
另外需要开long long导致MLE,所以需要滚动数组。
#include <cstdio> #define y(i , d) (sum[i] * sum[i] - f[i][d]) typedef long long ll; ll sum[100010] , f[100010][2]; int q[100010]; int main() { int n , k , i , j , l , r , d = 0; scanf("%d%d" , &n , &k); for(i = 1 ; i <= n ; i ++ ) scanf("%lld" , &sum[i]) , sum[i] += sum[i - 1]; for(j = 1 ; j <= k ; j ++ ) { l = r = 0 , d ^= 1; for(i = 1 ; i <= n ; i ++ ) { while(l < r && y(q[l + 1] , d ^ 1) - y(q[l] , d ^ 1) <= sum[i] * (sum[q[l + 1]] - sum[q[l]])) l ++ ; f[i][d] = f[q[l]][d ^ 1] + (sum[i] - sum[q[l]]) * sum[q[l]]; while(l < r && (y(i , d ^ 1) - y(q[r] , d ^ 1)) * (sum[q[r]] - sum[q[r - 1]]) <= (y(q[r] , d ^ 1) - y(q[r - 1] , d ^ 1)) * (sum[i] - sum[q[r]])) r -- ; q[++r] = i; } } printf("%lld\\n" , f[n][k & 1]); return 0; }
以上是关于bzoj3675[Apio2014]序列分割 斜率优化dp的主要内容,如果未能解决你的问题,请参考以下文章