[APIO2014] Sequence Segmentation---Slope Optimization DP

[APIO2014] Sequence Segmentation

Topic meaning:

 You are playing a game about sequences of non-negative integers of length \( n\) . In this game you need to divide the sequence into \( k + 1\)  non-empty chunks. To get \( k + 1\) blocks, you need to repeat the following operations \( k  \) times:

select a block with more than one element (initially you only have one block, i.e. the entire sequence)

Select two adjacent elements to split the block in the middle, resulting in two non-empty blocks.

After each operation you will get the score for the product of the element-wise sum of those two newly generated blocks. You want to maximize the final total score.

 

After first dividing the play\(k\) blocks, it is found that it is very similar to the linear DP model

Naturally, is it true that the score has nothing to do with the order of the strokes?

provably yes (induction)

Then, let

\(dp(i,j)\) means that the enumeration reaches \(i\), and the first \(1...i\) cuts the maximum profit.

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

Then expand the formula and turn it into a slope-optimized formula:

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

Where \(k\) is \(sum[k]\), monotonically increasing

Where \(x\) is \(sum[i]\), monotonically increasing

To maximize \(dp(i,j)\) and thus maintain the lower convex hull, you can use a monotonic queue

Just roll the space

 

Space complexity: \(O(n)\) (ignoring record decision points)

Time complexity: \(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;
}
sequence segmentation

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325165589&siteId=291194637