Codeforces Round #426 (Div. 2) D. The Bakery(DP+线段树)

传送门

题意:

将一个含n个元素的数列分成k段,每一段的值为其中不同元素的个数,问k段字符和的最大值

思路:

1、dp[i][j] 表示到j为止分成i段的最大值,

dp[i][j] = max(dp[i][j] , dp[i-1][x]+f(x+1,j)) (i-1<=x<=j-1,f(x+1,j)表示从x+1到j不同元素的个数

2、容易想到从1到k枚举,关键是利用线段树实现 dp[i-1][x]+f(x+1,j);

3、答案为dp[k][n]。


#include <bits/stdc++.h>
using namespace std;
#define N 35005
#define M 55
int dp[M][N];
int pre[N];
int index[N];
int maxq[N<<2];
int rem[N<<2];
void Push_down(int p)
{
    rem[p<<1] += rem[p];
    rem[p<<1|1] += rem[p];
    maxq[p] += rem[p];
    rem[p] = 0;
}
void Push_up(int p)
{
    maxq[p] = max(maxq[p<<1]+rem[p<<1],maxq[p<<1|1]+rem[p<<1|1]);
}
void update(int p,int l,int r,int ll,int rr,int num)
{
   if(l==ll && r==rr){
    rem[p] += num;
    return ;
   }
   if(rem[p]) Push_down(p);
   int mid = l + r >> 1;
   if(rr <= mid) update(p<<1,l,mid,ll,rr,num);
   else if(ll > mid) update(p<<1|1,mid+1,r,ll,rr,num);
   else{
    update(p<<1,l,mid,ll,mid,num);
    update(p<<1|1,mid+1,r,mid+1,rr,num);
   }
   Push_up(p);
}
int query(int p,int l,int r,int ll,int rr)
{
    if(l==ll && r==rr){
        return maxq[p] + rem[p];
    }
    if(rem[p]) Push_down(p);
    int mid = l + r >> 1;
    if(rr<=mid) return query(p<<1,l,mid,ll,rr);
    else if(ll>mid) return query(p<<1|1,mid+1,r,ll,rr);
    else return max(query(p<<1,l,mid,ll,mid),query(p<<1|1,mid+1,r,mid+1,rr));
}
int main()
{
    int n,k;
    cin>>n>>k;
    for(int i=1;i<=n;i++){
        int a;
        scanf("%d",&a);
        pre[i] = index[a];                  //pre[i] 表示 第 i 位的数上一次出现的位置,若第一次出现,则上一次的位置为0
        index[a] = i;
    }
    for(int i=1;i<=k;i++){
        memset(rem,0,sizeof(rem));
        memset(maxq,0,sizeof(maxq));
        for(int j=1;j<=n;j++){
            update(1,0,n,j,j,dp[i-1][j]);   //每一位初始化为上一次的答案
        }
        for(int j=i;j<=n;j++){ 
            update(1,0,n,pre[j],j-1,1);     //第j位出现的数,会影响 从 上一次该数出现的位置 到 j-1 的数
            dp[i][j] = query(1,0,n,i-1,j-1);
        }
    }
    printf("%d\n",dp[k][n]);
    return 0;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值