【动态规划】【线段树】 Codeforces Round #426 (Div. 1) B. The Bakery

给你一个序列,让你划分成K段,每段的价值是其内部权值的种类数,让你最大化所有段的价值之和。

裸dp

f(i,j)=max{f(k,j-1)+w(k+1,i)}(0<=k<i)

先枚举j,然后枚举i的时候,用线段树进行优化,对a(i)上一次出现的位置到i之间的f(k,j-1)的答案进行+1,然后求个i的前缀max。

要注意线段树区间加的时候其实要包含上0。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r
int n,m,a[35010],f[35010][60];
int maxv[35010<<2];
int delta[35010<<2];
void pushdown(int rt)//将rt结点的懒惰标记下传 
{
	if(delta[rt])
	  {
		delta[rt<<1]+=delta[rt];//标记下传到左结点 
		delta[rt<<1|1]+=delta[rt];//标记下传到右结点 
		maxv[rt<<1]+=delta[rt];
		maxv[rt<<1|1]+=delta[rt];
		delta[rt]=0;
	  }
}
void update(int ql,int qr,int v,int rt,int l,int r)
{
	if(ql<=l&&r<=qr)
	  {
		delta[rt]+=v;//更新当前结点的标记值 
		maxv[rt]+=v;
		return ;
	  }
	pushdown(rt);//将该节点的标记下传到孩子们 
	int m=(l+r)>>1;
	if(ql<=m)
	  update(ql,qr,v,lson);
	if(m<qr)
	  update(ql,qr,v,rson);
	maxv[rt]=max(maxv[rt<<1],maxv[rt<<1|1]);
}
int query(int ql,int qr,int rt,int l,int r)
{
	if(ql<=l&&r<=qr)
	  return maxv[rt];
	pushdown(rt);//将该节点的标记下传到孩子们 
	int m=(l+r)>>1;
	int res=-2147483647;
	if(ql<=m)
	  res=max(res,query(ql,qr,lson));
	if(m<qr)
	  res=max(res,query(ql,qr,rson));
	return res;
}
int now[35010],last[35010];
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i){
		scanf("%d",&a[i]);
	}
	for(int i=1;i<=n;++i){
		last[i]=now[a[i]];
		now[a[i]]=i;
	}
	for(int j=1;j<=m;++j){
		if(j!=1){
			memset(maxv,0,sizeof(maxv));
			memset(delta,0,sizeof(delta));
			for(int i=j-1;i<=n;++i){
				update(i,i,f[i][j-1],1,0,n);
			}
		}
		update(max(last[j],j-1),j-1,1,1,0,n);
		f[j][j]=j;
		for(int i=j+1;i<=n;++i){
			update(max(last[i],j-1),i-1,1,1,0,n);
			f[i][j]=query(j-1,i-1,1,0,n);
		}
	}
	printf("%d\n",f[n][m]);
	return 0;
}

转载于:https://www.cnblogs.com/autsky-jadek/p/7261165.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值