题解 CF833B 【The Bakery】


subject:
长度为n的序列a,将其分成连续的k段,每段的价值为其中数字种类的个数,求最大价值总和。

dp[i][k]:前i个数分成k段的最大价值

w(l,r):l到r产生的价值

dp[i][k]=max(dp[j][k-1]+w(j+1,i))

由于决策单调性,可以用分治求解
设已经算到k段对于当前区间[l,r],决策区间为[L,R],暴力求出dp[mid][k]的最佳决策点Mid,
再把区间分成[l,mid,L,Mid],[mid+1,r,Mid+1,R]继续递归

另:
关于求区间内不同值个数的两种主席树写法

bef[i]:上一个val与i相等的坐标

1.单点更新,区间查询,用到了差分思想
建一颗新树,bef[i]--,i++

2.区间查询,单点更新
建一颗新树(bef[i],i]++

下面用的是写法2

#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define debug puts("debug");
const int M=35e3+5,P=1e9+7,base=269;
int n,m,a[M],Tree[M*40],add[M*40],ls[M*40],rs[M*40],bef[M],tot,dp[M][55],nxt[M];
void build(int &rt,int l,int r){
	if(!rt)rt=++tot;
	if(l==r)return ;
	int mid=l+r>>1;
	build(ls[rt],l,mid);
	build(rs[rt],mid+1,r);
}
void update(int ot,int &rt,int l,int r,int L,int R){
	rt=++tot;
	ls[rt]=ls[ot];
	rs[rt]=rs[ot];
	add[rt]=add[ot];
	if(l>=L&&r<=R){
		add[rt]++;
		return ;
	}
	int mid=l+r>>1;
	if(R<=mid)update(ls[ot],ls[rt],l,mid,L,R);
	else if(L>mid)update(rs[ot],rs[rt],mid+1,r,L,R);
	else {
		update(ls[ot],ls[rt],l,mid,L,mid);
		update(rs[ot],rs[rt],mid+1,r,mid+1,R);
	}
}
int query(int rt,int l,int r,int x,int sum){
	sum+=add[rt];
	if(l==r)return sum;
	int mid=l+r>>1;
	if(x<=mid)return query(ls[rt],l,mid,x,sum);
	return query(rs[rt],mid+1,r,x,sum);
}
void solve(int l,int r,int L,int R,int k){
	int mid=l+r>>1;
	int w=0,Mid=0;
	if(R+2<=mid)w=query(Tree[mid],1,n,R+2,0);
	for(int i=min(R,mid-1);i>=L;--i){
		if(nxt[i+1]>mid)w++;
		if(dp[i][k-1]+w>dp[mid][k]){
			dp[mid][k]=dp[i][k-1]+w;
			Mid=i;
		}
	}
	if(l<=mid-1)solve(l,mid-1,L,Mid,k);
	if(r>=mid+1)solve(mid+1,r,Mid,R,k);
}
int main(){
	scanf("%d%d",&n,&m);
	build(Tree[0],1,n);
	for(int i=1;i<=n;++i){
		scanf("%d",&a[i]);
		update(Tree[i-1],Tree[i],1,n,bef[a[i]]+1,i);
		nxt[bef[a[i]]]=i;
		bef[a[i]]=i;
		nxt[i]=n+1;	
	}
	for(int i=1;i<=m;++i)
		solve(1,n,0,n-1,i);
	printf("%d\n",dp[n][m]);
	return 0;
}

posted @ 2021-06-02 19:35  We269  阅读(121)  评论(0)    收藏  举报