The Bakery
题意:
将一些数分成k段,每段的价值是里面不同数的个数,求能分得的最大值。
思路:
设dp方程 dp[i][j] 表示前 j个数分成 i段的最大值。转移方程 dp[i][j]=dp[i-1][x]+val[x]。1<=x<=j 。那么我们用线段树维护1到 j 的最大价值。对于每一个位置,找到前面最后一个与它数字相同的的位置,把这之间线段树的值都加上1。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=35005;
int n,k;
int dp[55][N];
int tr[N<<2],tag[N<<2];
int a[N],pre[N],pos[N];
void pushup(int o){
tr[o]=max(tr[o*2+1],tr[o*2]);
}
void pushdown(int o){
tr[o*2]+=tag[o];
tr[o*2+1]+=tag[o];
tag[o*2]+=tag[o];
tag[o*2+1]+=tag[o];
tag[o]=0;
}
void build(int o,int l,int r,int pos){
tag[o]=0;
if(l==r){
tr[o]=dp[pos][l-1];
return;
}
int m=(l+r)>>1;
build(o*2,l,m,pos);
build(o*2+1,m+1,r,pos);
pushup(o);
}
void up(int o,int l,int r,int ql,int qr){
if(ql<=l&&qr>=r){
tr[o]++;
tag[o]++;
return;
}
pushdown(o);
int m=(l+r)>>1;
if(ql<=m)
up(o*2,l,m,ql,qr);
if(qr>m)
up(o*2+1,m+1,r,ql,qr);
pushup(o);
}
int qu(int o,int l,int r,int ql,int qr){
if(ql<=l&&qr>=r)
return tr[o];
pushdown(o);
int m=(l+r)>>1;
int ans=0;
if(ql<=m)
ans=max(ans,qu(o*2,l,m,ql,qr));
if(qr>m)
ans=max(ans,qu(o*2+1,m+1,r,ql,qr));
pushup(o);
return ans;
}
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
pre[i]=pos[a[i]]+1;
pos[a[i]]=i;
}
for(int i=1;i<=k;i++){
build(1,1,n,i-1);
for(int j=1;j<=n;j++){
up(1,1,n,pre[j],j);
dp[i][j]=qu(1,1,n,1,j);
}
}
printf("%d\n",dp[k][n]);
return 0;
}