• 【APIO2014】序列分割


    题目链接

    【APIO2014】序列分割

    题目描述:

    你正在玩一个关于长度为 n 的非负整数序列的游戏。这个游戏中你需要把序列分成 k + 1 个非空的块。为了得到 k + 1块,你需要重复下面的操作 k 次:

    选择一个有超过一个元素的块(初始时你只有一块,即整个序列)

    选择两个相邻元素把这个块从中间分开,得到两个非空的块。

    每次操作后你将获得那两个新产生的块的元素和的乘积的分数。你想要最大化最后的总得分。

    输入输出格式

    输入格式:

    第一行包含两个整数 (n)(k)。保证 (k + 1 leq n)
    第二行包含 (n) 个非负整数 (a_1, a_2, cdots, a_n (0 leq a_i leq 10^4)),表示前文所述的序列。

    输出格式:

    第一行输出你能获得的最大总得分。

    第二行输出 (k) 个介于 (1)(n - 1) 之间的整数,表示为了使得总得分最大,你每次操作中分开两个块的位置。第 (i) 个整数 (s_i) 表示第 (i) 次操作将在 (s_i) 和 $s_{i + 1} $之间把块分开。

    如果有多种方案使得总得分最大,输出任意一种方案即可。

    输入输出样例

    输入样例#1:

    7 3
    4 1 3 4 0 2 3

    输出样例#1:

    108
    1 3 5

    说明

    你可以通过下面这些操作获得 (108) 分:

    初始时你有一块 ((4, 1, 3, 4, 0, 2, 3))。在第 (1) 个元素后面分开,获得 (4 imes (1 + 3 + 4 + 0 + 2 + 3) = 52) 分。

    你现在有两块 ((4), (1, 3, 4, 0, 2, 3))。在第 (3) 个元素后面分开,获得 ((1 + 3) imes (4 + 0 + 2 + 3) = 36) 分。

    你现在有三块 ((4), (1, 3), (4, 0, 2, 3))。在第 (5) 个元素后面分开,获得 ((4 + 0) imes (2 + 3) = 20) 分。

    所以,经过这些操作后你可以获得四块 ((4), (1, 3), (4, 0), (2, 3)) 并获得 (52 + 36 + 20 = 108) 分。

    限制与约定

    第一个子任务共 (11) 分,满足 (1 leq k < n leq 10)

    第二个子任务共 (11) 分,满足 (1 leq k < n leq 50)

    第三个子任务共 (11) 分,满足 (1 leq k < n leq 200)

    第四个子任务共 (17) 分,满足 (2 leq n leq 1000, 1 leq k leq min{n - 1, 200})

    第五个子任务共 (21) 分,满足 (2 leq n leq 10000, 1 leq k leq min{n - 1, 200})

    第六个子任务共 (29) 分,满足 (2 leq n leq 100000, 1 leq k leq min{n - 1, 200})

    题解

    在解决这个问题之前,我们先大胆口胡一个结论:分割的顺序不影响最后的结果。
    接下来就是这个结论的证明了:
    设两次分割把队列分为了A,B,C三个部分。

    1. 先分割A,B之间,再分割B,C之间:(ans=A*(B+C)+B*C=A*B+A*C+B*C)
    2. 先分割B,C之间,再分割A,B之间:(ans=C*(A+B)+A*B=A*B+A*C+B*C)

    由此可以得出分割的顺序不影响最后的结果。
    接下来我们就可以进行dp了。
    这是一个很明显的二维dp。
    (dp[i][k])表示前(i)个数被分割了(k)次所能得到的最大的答案。
    转移方程为(dp[i][k]=max(dp[j][k-1]+sum_{u=1}^{j}a[u]*sum_{u=j+1}^{i}a[u]))(在第(k)个数和第(k+1)个数之间分割)
    证明:
    假设(dp[j][k-1])最右边的分割点为(o)(o+1)(从(j)转移后(dp[i][k])最后),
    (dp[i][k-1]=dp[j][k-1]+sum_{u=1}^{o}a[u]*sum_{u=j+1}^{i}a[u])
    所以(dp[i][k]=dp[i][k-1]+sum_{u=o+1}^{j}a[u]*sum_{u=j+1}^{i}a[u])
    (=dp[j][k-1]+sum_{u=1}^{o}a[u]*sum_{u=j+1}^{i}a[u]+sum_{u=o+1}^{j}a[u]*sum_{u=j+1}^{i}a[u])
    (=dp[j][k-1]+sum_{u=1}^{j}a[u]*sum_{u=j+1}^{i}a[u])
    接下来我们用前缀和减小时间复杂度,
    (sum[i]=sum_{j=1}^{i}a[j])
    所以(dp[i][k]=max(dp[j][k-1]+sum[j]*(sum[i]-sum[j])))
    这个转移方程时间复杂度为O(n^2),因为(2 leq n leq 100000)所以我们用斜率优化。

    重点:
    我们取(j)(p)满足 (p<j<i)(j)(k)更优,那么有如下不等式:

    [dp[j][k-1]+sum[j]*(sum[i]-sum[j])>dp[p][k-1]+sum[p]*(sum[i]-sum[p]) ]

    化简后得:

    [frac{(dp[j][k-1]-sum[j]^2)-(dp[p][k-1]-sum[p]^2)}{sum[p]-sum[j]}leq sum[i] ]

    接下来维护一个下凸壳就可以了。
    注意:题目中(a[i])的范围是非负整数,所以(a[i])可以为0,计算时需要特判。
    上代码:

    #include<bits/stdc++.h>
    #define inf 1e18
    using namespace std;
    int n,k;
    long long a[100009],s[100009];
    long long dp[100009];
    long long pp[1000009],g[100009];
    long long l=1,r=1;
    double js(int j,int k){
        if(s[j]==s[k]) return -inf;
        return 1.0*(g[k]-g[j]+s[j]*s[j]-s[k]*s[k])/(s[j]-s[k]);
    }
    int main(){
        scanf("%d%d",&n,&k);
        for(int j=1;j<=n;j++)
            scanf("%lld",&a[j]);
        for(int j=1;j<=n;j++)
            s[j]=s[j-1]+a[j];
        for(int o=1;o<=k;o++){
            l=1;r=1;
            pp[1]=0;
            for(int j=1;j<=n;j++){
                while(l<r && js(pp[l+1],pp[l])<s[j]) l++;
                dp[j]=g[pp[l]]+(s[j]-s[pp[l]])*s[pp[l]];
                while(l<r && js(pp[r],pp[r-1])>js(j,pp[r])) r--;
                pp[++r]=j;
            }
            for(int j=1;j<=n;j++)
                g[j]=dp[j];
        }
        printf("%lld",dp[n]);
        return 0;
    }
    
    
  • 相关阅读:
    Hibernate逍遥游记-第10章 映射继承关系-001继承关系树中的每个具体类对应一个表
    Hibernate逍遥游记-第9章 Hibernate的映射类型
    Hibernate逍遥游记-第8章 映射组成关系(<component>、<parent>)
    Hibernate逍遥游记-第7章 Hibernate的检索策略和检索方式(<set lazy="false" fetch="join">、left join fetch、FetchMode.JOIN、)
    Hibernate逍遥游记-第6章 通过Hibernate操纵对象(select-before-update)
    Hibernate逍遥游记-第5章映射一对多-02双向(<set>、<key>、<one-to-many>、inverse、cascade="all-delete-orphan")
    Hibernate逍遥游记-第5章映射一对多-01单向<many-to-one>、cascade="save-update"、lazy、TransientObjectException
    Hibernate逍遥游记-第4章映射对象标识符-increment、identity、hilo、native、assigned、sequence、<meta>
    Hibernate逍遥游记-第3章对象-关系映射基础-access="field"、dynamic-insert、dynamic-update、formula、update=false
    CentOS 6.5安装Apache
  • 原文地址:https://www.cnblogs.com/linjiale/p/11213387.html
Copyright © 2020-2023  润新知