code force 833B The Bakery

来源:互联网 发布:淘宝怎么设置客服认证 编辑:程序博客网 时间:2024/06/10 02:58

题意可以总结为把一堆数分割成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;}
原创粉丝点击