您的位置:首页 > 其它

Codeforces Round #426 (Div. 2) D. The Bakery(DP+线段树)

2017-07-31 21:16 295 查看
传送门

题意:

将一个含n个元素的数列分成k段,每一段的值为其中不同元素的个数,问k段字符和的最大值

思路:

1、dp[i][j] 表示到j为止分成i段的最大值,

dp[i][j] = max(dp[i][j] , dp[i-1][x]+f(x+1,j)) (i-1<=x<=j-1,f(x+1,j)表示从x+1到j不同元素的个数;

2、容易想到从1到k枚举,关键是利用线段树实现 dp[i-1][x]+f(x+1,j);

3、答案为dp[k]


#include <bits/stdc++.h>
using namespace std;
#define N 35005
#define M 55
int dp[M]
;
int pre
;
int index
;
int maxq[N<<2];
int rem[N<<2];
void Push_down(int p)
{
rem[p<<1] += rem[p];
rem[p<<1|1] += rem[p];
maxq[p] += rem[p];
rem[p] = 0;
}
void Push_up(int p)
{
maxq[p] = max(maxq[p<<1]+rem[p<<1],maxq[p<<1|1]+rem[p<<1|1]);
}
void update(int p,int l,int r,int ll,int rr,int num)
{
if(l==ll && r==rr){
rem[p] += num;
return ;
}
if(rem[p]) Push_down(p);
int mid = l + r >> 1;
if(rr <= mid) update(p<<1,l,mid,ll,rr,num);
else if(ll > mid) update(p<<1|1,mid+1,r,ll,rr,num);
else{
update(p<<1,l,mid,ll,mid,num);
update(p<<1|1,mid+1,r,mid+1,rr,num);
}
Push_up(p);
}
int query(int p,int l,int r,int ll,int rr)
{
if(l==ll && r==rr){
return maxq[p] + rem[p];
}
if(rem[p]) Push_down(p);
int mid = l + r >> 1;
if(rr<=mid) return query(p<<1,l,mid,ll,rr);
else if(ll>mid) return query(p<<1|1,mid+1,r,ll,rr);
else return max(query(p<<1,l,mid,ll,mid),query(p<<1|1,mid+1,r,mid+1,rr));
}
int main()
{
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++){
int a;
scanf("%d",&a);
pre[i] = index[a];                  //pre[i] 表示 第 i 位的数上一次出现的位置,若第一次出现,则上一次的位置为0
index[a] = i;
}
for(int i=1;i<=k;i++){
memset(rem,0,sizeof(rem));
memset(maxq,0,sizeof(maxq));
for(int j=1;j<=n;j++){
update(1,0,n,j,j,dp[i-1][j]);   //每一位初始化为上一次的答案
}
for(int j=i;j<=n;j++){
update(1,0,n,pre[j],j-1,1);     //第j位出现的数,会影响 从 上一次该数出现的位置 到 j-1 的数
dp[i][j] = query(1,0,n,i-1,j-1);
}
}
printf("%d\n",dp[k]
);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息