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

Posted ReMoon

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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\n",dp[now][n]);
    int e = n;
    for(ri i = k; i >= 1; i --) {
        e = lst[i][e]; printf("%d ", e);
    }
    return 0;
}
序列分割

 

以上是关于[APIO2014]序列分割 --- 斜率优化DP的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ3675APIO2014序列分割 [斜率优化DP]

bzoj3675[Apio2014]序列分割 斜率优化dp

P3648 [APIO2014]序列分割 斜率优化

[APIO2014]序列分割

bzoj 3675 [Apio2014]序列分割(斜率DP)

[APIO2014]序列分割