CodeForces - 833B The Bakery

Posted on 2022-11-21 19:39  Capterlliar  阅读(8)  评论(0编辑  收藏  举报

看题解时全程:wow

题意:给出n个数,将其按顺序分为k组,令每组的价值为该组不同的数的个数。求一种分法,使得所有组价值和最大。

解:设dp[i][j]为将前 j 个数分为 i 组时的最大价值,显然有dp[i][j]=max(dp[i-1][k]+sum[k+1...j]),sum[k+1...j]为[k+1...j]中不同数的个数。然后优化求解过程,先解决sum怎么求。设原数组为a[i],令c[i]为区间[i, n]内不同数的个数,pre[i]为a[i]最后一次出现的位置,那么对于每一个i,c[pre[i]+1...i]+1,最终结果即为数组定义。意思是在pre[i]+1到i之间没有出现过a[i],那么它可以对答案贡献1,并且这样做不会有重复加的情况出现。

以样例3为例:

a:   7 7 8 7 7 8 1 7

pre+1:1 2 1 3 5 4 1 6

index:   1 2 3 4 5 6 7 8

c:   3 3 3 3 3 3 2 1

然后再来看这道题。通常我们先枚举i,再枚举j,也就是一个数一个数往里加。每添加一个数j,就可以获得所有的[k+1...j],很快乐,直接在dp[i-1]上加然后求最大值即可。区间加和求最大值的数据结构显然用线段树。注意给dp[i][k]加上c[k+1]的值,不是c[k]。

代码:

#include <bits/stdc++.h>
using namespace std;
#define maxx 35005
#define maxn 25
#define maxm 205
#define ll long long
#define inf 1000000009
#define mod 2520
int a[maxx]={0};
int dp[55][maxx]={0};
int pre[maxx]={0},pos[maxx]={0};
struct node{
    int val,lazy;
}tr[maxx*4];
void pushup(int now){
    tr[now].val=max(tr[now*2].val,tr[now*2+1].val);
}
void pushdown(int now){
    if(!tr[now].lazy){
        return;
    }
    tr[now*2].lazy+=tr[now].lazy;
    tr[now*2+1].lazy+=tr[now].lazy;
    tr[now*2].val+=tr[now].lazy;
    tr[now*2+1].val+=tr[now].lazy;
    tr[now].lazy=0;
}
int ii;
void build(int now,int l,int r){
    if(l==r){
        tr[now].val=dp[ii][l-1];
        tr[now].lazy=0;
        return ;
    }
    tr[now].lazy=0;
    int mid=(l+r)/2;
    build(now*2,l,mid);
    build(now*2+1,mid+1,r);
    pushup(now);
}
void add(int now,int l,int r,int s,int e){
    if(s<=l&&r<=e){
        tr[now].val+=1;
        tr[now].lazy+=1;
        return;
    }
    pushdown(now);
    int mid=(l+r)/2;
    if(s<=mid) add(now*2,l,mid,s,e);
    if(mid<e) add(now*2+1,mid+1,r,s,e);
    pushup(now);
}
int query(int now,int l,int r,int s,int e){
    if(s<=l&&r<=e){
        return tr[now].val;
    }
    pushdown(now);
    int mid=(l+r)/2;
    int res=0;
    if(s<=mid) res=max(res, query(now*2,l,mid,s,e));
    if(mid<e) res=max(res, query(now*2+1,mid+1,r,s,e));
    return res;
}

signed main() {
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    for(int i=1;i<=n;i++){
        pre[i]=pos[a[i]]+1;
        pos[a[i]]=i;
    }
    for(int i=1;i<=k;i++){
        ii=i-1;
        build(1,1,n);
        for(int j=1;j<=n;j++){
            add(1,1,n,pre[j],j);
            dp[i][j]= query(1,1,n,1,j);
        }
    }
    printf("%d\n",dp[k][n]);
    return 0;
}
//dp[i][j] the first j th num, distribute into i blocks
//dp[i][j]=max(dp[i-1][k]+sum[k+1...j])
View Code