code force 833B The Bakery

题意可以总结为把一堆数分割成k份,每份的权值定义为里面不同数个数,要让总的权值最大。


这种类型的题目第一反应要想到用dp,有两个参数,第一维是k份,第二维是物品个数。dp[i][j]。
然后分析转移。
对于某个物品,他要产生贡献,则必定是到上一次出现这个4号之前的位置都能加一。而这个位置而言指的是如果在这个位置断开,新开一个区间来装4这个物品就能总的权值加1,比如说在这个物品的前一个4还要前的位置断开,这个区间就有两个4了,并不能让总的权值加1。
这里写图片描述
理解完状态的转移之后,可以用n的平方方法来往前找dp[i][j]=MAX(dp[i-1][0~prej-1],dp[i-1][prej~j-1]+1)。这就像一个前一个dp中来一个区间更新,然后再找查询0~j-1中的dp最大值进行转移。也就是很像线段树的操作,所以可以用线段树进行维护这个dp转移。

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 35000+10;
int n,k;
int cake[MAXN];
class Seg//线段树查询区间最大值
{
    int maxv[MAXN*4];
    int add[MAXN*4];
    void pushdown(int rt)
    {
        if(add[rt])
        {
            add[rt<<1]+=add[rt];
            add[rt<<1|1]+=add[rt];
            maxv[rt<<1]+=add[rt];
            maxv[rt<<1|1]+=add[rt];
            add[rt]=0;
        }
    }
    void pushup(int rt)
    {
        maxv[rt]=max(maxv[rt<<1],maxv[rt<<1|1]);
    }
public:
    void build(int rt,int l,int r)
    {
        add[rt]=0;
        if(l==r)
        {
            maxv[rt]=add[rt]=0;
            pushup(rt);
            return;
        }
        int m=l+r>>1;
        build(rt<<1,l,m);
        build(rt<<1|1,m+1,r);
        pushup(rt);
    }
    void updata(int rt,int l,int r,int left,int right,int value)
    {
        if(l>=left&&r<=right)
        {
            maxv[rt]+=value;
            add[rt]+=value;
            return;
        }
        pushdown(rt);
        int m=l+r>>1;
        if(left<=m) updata(rt<<1,l,m,left,right,value);
        if(right>=m+1) updata(rt<<1|1,m+1,r,left,right,value);
        pushup(rt);
    }
    int query(int rt,int l,int r,int left,int right)
    {
        if(left<=l&&right>=r)
            return maxv[rt];
        if(left>r||right<l)
            return 0;
        pushdown(rt);
        int m=l+r>>1;
        return max(query(rt<<1,l,m,left,right),query(rt<<1|1,m+1,r,left,right));
    }
}seg[55];
int last[MAXN];
int pos[MAXN];
int main()
{
    if (fopen("in.txt", "r") != NULL)
    {
        freopen("in.txt", "r", stdin);
        // freopen("out.txt", "w", stdout);
    }
    cin>>n>>k;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",cake+i);
        last[i]=pos[cake[i]];//这个颜色上一次出现的位置
        pos[cake[i]]=i;//更新位置
    }
    seg[0].build(1,0,n);
    for(int i=1;i<=k;i++)
    {
        seg[i].build(1,0,n);
        for(int j=1;j<=n;j++)
        {
            seg[i-1].updata(1,0,n,last[j],j-1,1);
            int dp=seg[i-1].query(1,0,n,0,j-1);
            seg[i].updata(1,0,n,j,j,dp);
        }
    }
    cout<<seg[k].query(1,0,n,0,n);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值