• [APIO2014]序列分割 --- 斜率优化DP


    [APIO2014]序列分割

    题目大意:

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

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

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

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

    (n<=10^{5},k<=200)

    首先划分完(k)块后,发现非常像线性DP模型

    自然地想,是不是分数跟划的顺序无关?

    可以证明是的(归纳法)

    那么,设

    (dp(i,j))表示枚举到了(i),第(1...i)切了几刀的最大收益。

    有(dp(i,j)=max(dp(k,j-1)+sum[k]*(sum[i]-sum[k]))(1<=k<=i-1))

    那么展开式子,化为斜率优化的式子:

    (-dp(k,j-1)=sum[k]*sum[i]-sum[k]^{2}-dp(i,j))

    其中(k)为(sum[k]),单调递增

    其中(x)为(sum[i]),单调递增

    要使(dp(i,j))最大,因此维护下凸包,那么可以使用单调队列

    空间滚一下就好

    空间复杂度:(O(n))(忽略记录决策点)

    时间复杂度:(O(nk))

    注:被宏定义坑了很久。。。

    #include<cstdio>
    #include<cstring>
    #define sid 100050
    #define dd double
    #define ll long long
    #define ri register int
    using namespace std;
    
    #define getchar() *S ++
    char RR[30000005], *S = RR;
    inline int read(){
        int p = 0, w = 1;
        char c = getchar();
        while(c > '9' || c < '0') {
            if(c == '-') w = -1;
            c =  getchar();
        }
        while(c >= '0' && c <= '9') {
            p = p * 10 + c - '0';
            c = getchar();
        }
        return p * w;
    }
    
    ll sum[sid], dp[2][sid];
    int lst[205][sid], q[sid], n, k;
    bool now = 0, pre = 1;
    
    #define x(g) sum[(g)]
    #define y(g) (sum[(g)]*sum[(g)]-dp[pre][(g)])
    inline dd s(int i, int j) {
        if(x(i) == x(j)) return -1e18;
        return (dd)(y(i) - y(j)) / (dd)(x(i) - x(j));
    }
    
    int main() {
        fread(RR, 1, sizeof(RR), stdin);
        n = read(); k = read();
        for(ri i = 1; i <= n; i ++) sum[i] = sum[i - 1] + read();
        for(ri j = 1; j <= k; j ++) {
            int fr = 1, to = 1; now ^= 1; pre ^= 1;
            for(ri i = 1; i <= n; i ++) {
                while(fr + 1 <= to && s(q[fr], q[fr + 1]) <= sum[i]) fr ++;
                dp[now][i] = dp[pre][q[fr]] + sum[q[fr]] * (sum[i] - sum[q[fr]]);
                lst[j][i]=q[fr];
                while(fr + 1 <= to && s(q[to - 1], q[to]) >= s(q[to], i)) to --;
                q[++ to] = i;
            }
        }
        printf("%lld
    ",dp[now][n]);
        int e = n;
        for(ri i = k; i >= 1; i --) {
            e = lst[i][e]; printf("%d ", e);
        }
        return 0;
    }
    序列分割
  • 相关阅读:
    k8s查看证书期限
    Calico 路由反射模式权威指南
    prometheus 数据传输到远程 VictoriaMetrics 存储
    node cnmp 安装
    redis3.2.8 版本部署 哨兵模式
    Centos搭建ffmpeg环境
    查看k8s controllersmanager scheduler 主
    监控rocketmq 常用方法
    apache 负载均衡http 配置
    JBoss AS 安装配置部署报错以及使用wildfly替换
  • 原文地址:https://www.cnblogs.com/reverymoon/p/8981610.html
Copyright © 2020-2023  润新知