码迷,mamicode.com
首页 > Windows程序 > 详细

BZOJ3675: [Apio2014]序列分割

时间:2018-03-15 21:09:10      阅读:291      评论:0      收藏:0      [点我收藏+]

标签:int   开始   ring   while   scanf   传送门   turn   algorithm   http   

【传送门: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<j2<i

  得到

  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[i]

  因为sum[j1]-sum[j2]有可能等于0,所以在处理队列的时候,如果存在sum[j1]-sum[j2],要将j1给除掉,保留j2

  注意加long long


参考代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long LL;
//f[i][now]=max(f[j][last]+sum[j]*(sum[i]-sum[j]))
//j1<j2<i
//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[i]
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<=n;i++)
    {
        int d;
        scanf("%d",&d);
        sum[i]=sum[i-1]+d;
    }
    memset(f,0,sizeof(f));
    for(int k=1;k<=K;k++)
    {
        now^=1;last^=1;
        int head=1,tail=0;list[head]=0;
        for(int i=k;i<=n;i++)
        {
            while(head<tail&&(sum[list[head]]==sum[list[head]+1]||slop(list[head],list[head+1])<sum[i])) head++;
            int j=list[head];
            f[i][now]=f[j][last]+sum[j]*(sum[i]-sum[j]);
            while(head<tail&&(sum[i]==sum[list[tail]]||slop(list[tail-1],list[tail])>slop(list[tail],i))) tail--;
            list[++tail]=i;
        }
    }
    printf("%lld\n",f[n][now]);
    return 0;
}

 

BZOJ3675: [Apio2014]序列分割

标签:int   开始   ring   while   scanf   传送门   turn   algorithm   http   

原文地址:https://www.cnblogs.com/Never-mind/p/8573276.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!