CF833B The Bakery

题目大意:给出n个数的数列,其中保证1<=ai<=n,使其分成m份,使得每一段字符集大小相加最大,求最大值。

考场上跪了,回来一想发现是线段树维护dp:

原dp:dp[ i ][ j ] = max ( dp[ j ][ k-1 ] + num[ j+1 ][ i ]);

然后发现线段树可以维护,即将等号右边的那一段扔到线段树中,时间复杂度为 O( nlogn )。

代码:

#include<cstdio>
#include<algorithm>
using namespace std;
#define N 35050
int n,K;
int a[N],las[N],nxt[N];
int v[N<<2],mrk[N<<2];
int dp[51][N];
void update(int u)
{
    v[u]=max(v[u<<1],v[u<<1|1]);
}
void add(int u,int d)
{
    v[u]+=d;
    mrk[u]+=d;
}
void pushdown(int u)
{
    if(mrk[u])
    {
        add(u<<1,mrk[u]);
        add(u<<1|1,mrk[u]);
        mrk[u]=0;
    }
}
void build(int l,int r,int u,int k)
{
    mrk[u]=0;
    if(l==r)
    {
        v[u]=dp[k][l-1];
        return ;
    }
    int mid = (l+r)>>1;
    build(l,mid,u<<1,k);
    build(mid+1,r,u<<1|1,k);
    update(u);
}
void insert(int l,int r,int u,int ql,int qr)
{
    if(l==ql&&r==qr)
    {
        add(u,1);
        return ;
    }
    pushdown(u);
    int mid = (l+r)>>1;
    if(ql>mid)insert(mid+1,r,u<<1|1,ql,qr);
    else if(qr<=mid)insert(l,mid,u<<1,ql,qr);
    else insert(l,mid,u<<1,ql,mid),insert(mid+1,r,u<<1|1,mid+1,qr);
    update(u);
}
int query(int l,int r,int u,int ql,int qr)
{
    if(l==ql&&r==qr)return v[u];
    pushdown(u);
    int mid = (l+r)>>1;
    if(ql>mid)return query(mid+1,r,u<<1|1,ql,qr);
    else if(qr<=mid)return query(l,mid,u<<1,ql,qr);
    else return max(query(l,mid,u<<1,ql,mid),query(mid+1,r,u<<1|1,mid+1,qr));
}
int main()
{
//    freopen("handsome.in","r",stdin);
//    freopen("handsome.out","w",stdout);
    scanf("%d%d",&n,&K);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        nxt[i]=las[a[i]];
        las[a[i]]=i;
    }
    for(int k=1;k<=K;k++)
    {
        build(1,n,1,k-1);
        for(int i=1;i<=n;i++)
        {
            insert(1,n,1,nxt[i]+1,i);
            dp[k][i]=query(1,n,1,1,i);
        }
    }
    printf("%d
",dp[K][n]);
    return 0;
}
原文地址:https://www.cnblogs.com/LiGuanlin1124/p/9736629.html