CodeForces - 833B The Bakery

xiaoxiao2021-02-28  70

题面

题意

给你n个数,将它们分成k组(不改变顺序),每组的价值为这组中数字的种数,求所有组的最大价值和。

做法

首先思考最朴素的dp: dp[i][j]表示将前i个数分成j组的最大价值和。 因此,dp[i][j]=max(dp[i-k][j-1]+val[i-k+1][i]). 时间复杂度为O(k*n^2). 考虑优化这个dp,可以先求出dp[i][1](这列数的前缀的价值),然后求出dp[i][2],dp[i][3]……dp[i][k],具体求dp[i][j]值的时候,可以将dp[i][j-1]的值存入线段树,逐个数考虑其贡献,发现每个数对答案做出的贡献可以转化为区间加,每次求出最大值即可。

代码

#include<iostream> #include<cstdio> #define mid ((l+r)>>1) #define N 35010 using namespace std; int n,k,num[N],last[N],pos[N],dp[N][60],tt=1,tmp; struct Node { int ls,rs,mx,sum; } node[N<<1]; void build(int now,int l,int r) { if(l==r) return; if(l<=mid) { node[now].ls=++tt; build(tt,l,mid); } if(mid<r) { node[now].rs=++tt; build(tt,mid+1,r); } } void add(int now,int l,int r,int u,int v,int w) { if(l==u&&v==r) { node[now].sum+=w; return; } if(u<=mid) { add(node[now].ls,l,mid,u,min(mid,v),w); } if(mid<v) { add(node[now].rs,mid+1,r,max(mid+1,u),v,w); } node[now].mx=max(node[node[now].ls].mx+node[node[now].ls].sum,node[node[now].rs].mx+node[node[now].rs].sum); } void ql(int now,int l,int r,int u) { node[now].sum=node[now].mx=0; if(l==r) return; if(l<=mid) { ql(node[now].ls,l,mid,u); } if(mid<r) { ql(node[now].rs,mid+1,r,u); } } void chg(int now,int l,int r,int u,int v) { if(l==r) { node[now].mx=v; return; } int res=0; if(u<=mid) { chg(node[now].ls,l,mid,u,v); } else { chg(node[now].rs,mid+1,r,u,v); } node[now].mx=max(node[node[now].ls].mx+node[node[now].ls].sum,node[node[now].rs].mx+node[node[now].rs].sum); } int main() { int i,j; cin>>n>>k; for(i=1; i<=n; i++) { scanf("%d",&num[i]); if(!pos[num[i]]) tmp++; last[i]=pos[num[i]]; pos[num[i]]=i; dp[i][1]=tmp; } build(1,1,n); for(i=2; i<=k; i++) { ql(1,1,n,i-1); for(j=1;j<i;j++) chg(1,1,n,j,dp[j][i-1]); for(j=i; j<=n; j++) { add(1,1,n,max(i-1,last[j]),j-1,1); dp[j][i]=node[1].mx+node[1].sum; chg(1,1,n,j,dp[j][i-1]); } } cout<<dp[n][k]; }
转载请注明原文地址: https://www.6miu.com/read-2619351.html

最新回复(0)