BZOJ3675: [Apio2014]序列分割

2021-04-21 11:28

阅读:495

【传送门: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])
LL 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)
    {
        now^=1;last^=1;
        int head=1,tail=0;list[head]=0;
        for(int i=k;i)
        {
            while(head1]||slop(list[head],list[head+1]);
            int j=list[head];
            f[i][now]=f[j][last]+sum[j]*(sum[i]-sum[j]);
            while(head1],list[tail])>slop(list[tail],i))) tail--;
            list[++tail]=i;
        }
    }
    printf("%lld\n",f[n][now]);
    return 0;
}

 


评论


亲,登录后才可以留言!