BZOJ3675: [Apio2014]序列分割
2021-04-21 11:28
【传送门:BZOJ3675】
简要题意:
一开始给出n个数的一段序列,可以分割k次,每次只能分割一段序列,一段序列被分割后就变成两个序列,每次分割的价值为分割的位置左边的数的和乘右边的数的和
求出最大价值
题解:
DP+斜率优化
首先来设f[i][k]为前i个数,分割k次得到的最大价值
接下来。。。
方程。。。。
方程。。。
方程。。
方程。
需要玄学思考
假设一段序列为4|1 2|3(|表示分割)
那么f[2][1]=4*1=4
f[4][2]=4*(1+2+3)+(1+2)*3=f[2][1]+(4+1)*(3+2)!!
其实这里用了乘法分配律
太棒了这样就得到f[i][k]=max(f[j][k-1]+sum[j]*(sum[i]-sum[j]))
然后滚动数组f[i][now]=max(f[j][last]+sum[j]*(sum[i]-sum[j]))
再用斜率优化,美滋滋
设j1
得到
f[j2][last]+sum[j2]*(sum[i]-sum[j2])>f[j1][last]+sum[j1]*(sum[i]-sum[j1])
f[j2][last]-f[j1][last]+sum[j1]^2-sum[j2]^2>(sum[j1]-sum[j2])*sum[i]
(f[j2][last]-f[j1][last]+sum[j1]^2-sum[j2]^2)/(sum[j1]-sum[j2])
因为sum[j1]-sum[j2]有可能等于0,所以在处理队列的时候,如果存在sum[j1]-sum[j2],要将j1给除掉,保留j2
注意加long long
参考代码:
#include#include #include #include #include using namespace std; typedef long long LL; //f[i][now]=max(f[j][last]+sum[j]*(sum[i]-sum[j])) //j1 //f[j2][last]+sum[j2]*(sum[i]-sum[j2])>f[j1][last]+sum[j1]*(sum[i]-sum[j1]) //f[j2][last]-f[j1][last]+sum[j1]^2-sum[j2]^2>(sum[j1]-sum[j2])*sum[i] //(f[j2][last]-f[j1][last]+sum[j1]^2-sum[j2]^2)/(sum[j1]-sum[j2]) ^=1;last^=1; int head=1,tail=0;list[head]=0; for(int i=k;i) { while(headLL f[110000][2]; LL sum[110000]; int now=1,last=0; LL slop(int j1,int j2) { return (f[j2][last]-f[j1][last]-sum[j2]*sum[j2]+sum[j1]*sum[j1])/(sum[j1]-sum[j2]); } int list[110000]; int main() { int n,K; scanf("%d%d",&n,&K); sum[0]=0; for(int i=1;i) { int d; scanf("%d",&d); sum[i]=sum[i-1]+d; } memset(f,0,sizeof(f)); for(int k=1;k) { now1]||slop(list[head],list[head+1]) ; int j=list[head]; f[i][now]=f[j][last]+sum[j]*(sum[i]-sum[j]); while(head 1],list[tail])>slop(list[tail],i))) tail--; list[++tail]=i; } } printf("%lld\n",f[n][now]); return 0; }
上一篇:第一课时之c#程序设计概述
下一篇:c#中this的用法及作用
文章标题:BZOJ3675: [Apio2014]序列分割
文章链接:http://soscw.com/index.php/essay/77586.html