• BZOJ3675: [Apio2014]序列分割


    【传送门:BZOJ3675


    简要题意:

      一开始给出n个数的一段序列,可以分割k次,每次只能分割一段序列,一段序列被分割后就变成两个序列,每次分割的价值为分割的位置左边的数的和乘右边的数的和

      求出最大价值


    题解:

      DP+斜率优化

      首先来设f[i][k]为前i个数,分割k次得到的最大价值

      接下来。。。

      方程。。。。

      方程。。。

      方程。。

      方程。

      需要玄学思考

      其实这里用了乘法分配律,证明分割的顺序是与答案无关的,只要割对地方就行了

      太棒了这样就得到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
    ",f[n][now]);
        return 0;
    }

     

  • 相关阅读:
    Linux实战教学笔记14:用户管理初级(上)
    上传文件到服务器指定位置 & 从服务器指定位置下载文件
    flume读取日志文件并存储到HDFS
    Kafka
    Eclipse获取工作空间跟运行空间
    Java开发webservice的几种方式
    获取.properties配置文件属性值
    数组合并
    字符串对象跟xml格式的转换
    文件压缩跟解压(本地&Linux服务器)
  • 原文地址:https://www.cnblogs.com/Never-mind/p/8573276.html
Copyright © 2020-2023  润新知