Codeforces Round #426 (Div. 1) B. The Bakery (DP + 线段树)

来源:互联网 发布:网络推手立二拆四 编辑:程序博客网 时间:2024/06/04 01:19

Problem

将 N 长序列划分 K 块的最大价值和。每块的价值定义为该段区间内的不同数的个数。

其中 N 长序列的每个数为 a1,a2,,an

Limit

1N35000

1Kmin(n,50)

1aiN

Idea

从数据限制来看,此题的复杂度一般应该在 O(NKlogN) 左右。

普遍的第一想法是 DP(也确实是 DP)。dp[i][k] 表示将前 i 个数共划分为 k 块的最大价值。

则状态转移为: dp[i][k] = max(dp[i][k], dp[j][k-1] + distinct(j+1, i)) (1<=j<i)

如何统计 distinct (j+1, i) 是这个转移方程的最大难点。

预处理每个位置数字的前一个相同数的位置 pre[i]。当统计分块为 k 时的所有前置状态均为 k-1 块。统计 distinct (j+1, i) 可以通过与 dp[i][k] 的 i 同步递增来达到 O(1) 获取。例如位置 i 的数要在第 k 块产生贡献,则其一定要时第 k 块中的第一个该数字(若第 k 块之前已经有对应数字,则 i 位置无贡献),此时就可以用预处理得到的 pre[i] ,所有的 dp[j][k-1] (j[pre[i],i1]) 对之后所有的可能都为 +1 .

对于这部分的 dp[j][k-1] 利用区间最大值线段树维护 update(pre[i], i-1, +1) 并查找 [1, i-1] 内的最大值即 dp[i][k] 的最大价值和。

Code

#include<bits/stdc++.h>using namespace std;const int N = 35000 + 10;const int K = 50 + 10;int n, k, a[N], pre[N], vis[N], dp[N][K];// Segment Tree #define lson l , m , rt << 1  #define rson m + 1 , r , rt << 1 | 1 const int MAXN = 35000 + 10;int add[MAXN<<2], mx[MAXN<<2];void PushUp(int rt) {   mx[rt] = max(mx[rt<<1], mx[rt<<1|1]);   }void PushDown(int rt, int m) {    if(add[rt]) {        add[rt<<1] += add[rt];        add[rt<<1|1] += add[rt];        mx[rt<<1] += add[rt];        mx[rt<<1|1] += add[rt];        add[rt] = 0;    }}void build(int l, int r, int rt, int ik) {    add[rt] = 0;    if(l == r) {        mx[rt] = dp[l][ik];        return;    }    int m = (l+r)>>1;    build(lson, ik);    build(rson, ik);    PushUp(rt);}void update(int L, int R, int w, int l, int r, int rt) {    if(L>R) return;    if(L <= l && r <= R) {        add[rt] += w;        mx[rt] += w;        return;    }    PushDown(rt, r-l+1);    int m = (l+r) >> 1;    if(L <= m)  update(L, R, w, lson);    if(m < R)   update(L, R, w, rson);    PushUp(rt);}int query(int L, int R, int l, int r, int rt) {    if(L <= l && r <= R)    return mx[rt];    PushDown(rt, r-l+1);    int m = (l+r) >> 1;    int ret = 0;    if(L <= m)  ret = max(ret, query(L, R, lson));    if(m < R)   ret = max(ret, query(L, R, rson));    return ret;}int main(){    scanf("%d %d", &n, &k);    for(int i=1;i<=n;i++)    {        scanf("%d", &a[i]);        if(vis[ a[i] ]) dp[i][1] = dp[i-1][1];        else    dp[i][1] = dp[i-1][1]+1;        pre[i] = max(vis[ a[i] ], 1);        vis[ a[i] ] = i;    }    for(int ik=2;ik<=k;ik++)     {        build(1, n, 1, ik-1);        dp[1][k] = 1;        for(int i=2;i<=n;i++)        {            update(pre[i], i-1, 1, 1, n, 1);            dp[i][ik] = max(dp[i][ik], query(1, i-1, 1, n, 1));        }    }    printf("%d\n", dp[n][k]);}
阅读全文
0 0