Codeforces 834D The Bakery
题目大意是给你一个长度为n的序列分成k段,每一段的贡献是这一段中不同的数的个数,求最大贡献
是第一次做线段树维护DP值的题
感觉还可以,虽然看了一下这题是用线段树维护DP值
然后说思路
有一个很显然的思路是这样的:
d
p
i
,
j
dp_{i,j}
dpi,j表示前i个数分成j段的最大贡献
d
p
i
,
j
=
m
a
x
(
d
p
k
,
j
−
1
+
c
a
l
c
(
k
+
1
,
i
)
)
dp_{i,j}=max(dp_{k,j-1}+calc(k+1,i))
dpi,j=max(dpk,j−1+calc(k+1,i))
然后我们就可以对于每一层j用线段树维护起来
然后就非常愉快地发现
d
p
[
i
]
[
p
]
只
会
从
d
p
[
i
−
1
]
[
p
−
1
]
dp[i][p]只会从dp[i-1][p-1]
dp[i][p]只会从dp[i−1][p−1]之前的DP值进行转移
所以可以发现每次可以错位建立线段树
然后现在考虑怎么维护
c
a
l
c
(
k
+
1
,
i
)
calc(k+1,i)
calc(k+1,i)
我们可以把一段区间的贡献拆成每个点的贡献
显然位置i的数会对左端点位置在
p
r
e
[
i
]
+
1
i
pre[i]+1~i
pre[i]+1 i的所有点产生加一的贡献(因为考虑的右端点在i)
所以每次判断一下区间加区间求和就好了
#include<bits/stdc++.h>
using namespace std;
#define N 350010
int a[N],b[N];
int dp[N][60];
int las[N],pre[N];
int n,k;
namespace Segment_Tree{
#define MAXN N<<2
#define LD (t<<1)
#define RD (t<<1|1)
int maxv[MAXN],add[MAXN];
void pushup(int t){maxv[t]=max(maxv[LD],maxv[RD]);}
void pushdown(int t){
if(add[t]){
maxv[LD]+=add[t],add[LD]+=add[t];
maxv[RD]+=add[t],add[RD]+=add[t];
add[t]=0;
}
}
void build(int t,int l,int r){
if(l>r)return;
add[t]=0;
if(l==r){maxv[t]=a[l];return;}
int mid=(l+r)>>1;
build(LD,l,mid);
build(RD,mid+1,r);
pushup(t);
}
void modify(int t,int l,int r,int L,int R){
if(l>r)return;
if(L<=l&&r<=R){maxv[t]++;add[t]++;return;}
pushdown(t);
int mid=(l+r)>>1;
if(R<=mid)modify(LD,l,mid,L,R);
else if(L>mid)modify(RD,mid+1,r,L,R);
else{
modify(LD,l,mid,L,mid);
modify(RD,mid+1,r,mid+1,R);
}
pushup(t);
}
int query(int t,int l,int r,int L,int R){
if(l>r)return 0;
if(L<=l&&r<=R)return maxv[t];
pushdown(t);
int mid=(l+r)>>1;
int ans=0;
if(R<=mid)ans=query(LD,l,mid,L,R);
else if(L>mid)ans=query(RD,mid+1,r,L,R);
else ans=max(query(LD,l,mid,L,mid),query(RD,mid+1,r,mid+1,R));
pushup(t);
return ans;
}
};
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)scanf("%d",&a[i]),b[i]=a[i];
sort(b+1,b+n+1);
int tot=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+tot+1,a[i])-b;
for(int i=1;i<=n;i++)pre[i]=las[a[i]],las[a[i]]=i;
for(int i=1;i<=n;i++)dp[i][1]=dp[i-1][1]+(int)(pre[i]==0);
for(int j=2;j<=k;j++){
for(int i=1;i<=n;i++)a[i]=dp[i-1][j-1];
Segment_Tree::build(1,1,n);
for(int i=1;i<=n;i++){
Segment_Tree::modify(1,1,n,pre[i]+1,i);
dp[i][j]=Segment_Tree::query(1,1,n,1,i);
}
}
printf("%d",dp[n][k]);
return 0;
}