题解 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;
}

浙公网安备 33010602011771号