题目链接:http://codeforces.com/contest/834/problem/D
题意:给定一个长度为n的序列和一个k,现在让你把这个序列分成刚好k段,并且k段的贡献之和最大。对于每一段的贡献为该段有多少个不同的数字。
思路:考虑dp, dp[k][i]表示前i个数切k段的答案,那么dp[k][i]=max(dp[k-1][j]+color(j+1,i)) [1<=j<i], [color(l,r)表示区间[l,r]有多少个不同的数字] ,由于第k行的dp值只会影响到k+1行的dp值,所以我们可以把k这一维忽略掉。考虑转移dp[k][i+1],新增加的i+1这个点会影响到[pre[a[i]]+1,i]这段区间+1。
ftiasch的题解
我们在建线段树的时候,树上的结点(结点表示的区间为[l,r])维护的是g[k](即dp[k-1][r-1]),然后就是区间加和区间最大值了。
#include <iostream> #include <cstdio> #include <cmath> #include <queue> #include <cstring> #include <algorithm> #include <map> #include <string> #include <bitset> using namespace std; typedef long long LL; const int MAXN = 35000 + 24; int n,k,pre[MAXN],dp[MAXN]; struct Color{ int val,l,r; }c[MAXN]; //Segment Tree #define L(x)(x<<1) #define R(x)(x<<1|1) struct Node{ int l,r,Lazy,maxval; Node(int _l=0,int _r=0,double _val=0){ l=_l; r=_r; maxval=_val; } }Seg[MAXN*4]; void pushUp(int k){ Seg[k].maxval=max(Seg[L(k)].maxval,Seg[R(k)].maxval); } void pushDown(int k){ if(Seg[k].Lazy){ Seg[L(k)].Lazy+=Seg[k].Lazy; Seg[L(k)].maxval+=Seg[k].Lazy; Seg[R(k)].Lazy+=Seg[k].Lazy; Seg[R(k)].maxval+=Seg[k].Lazy; Seg[k].Lazy=0; } } void Build(int st,int ed,int k){ Seg[k].l=st; Seg[k].r=ed; Seg[k].Lazy=0; if(st==ed){ Seg[k].maxval=dp[st-1]; return; } int mid=(st+ed)>>1; Build(st,mid,L(k)); Build(mid+1,ed,R(k)); pushUp(k); } void Modify(int st,int ed,int k,int val){ if(Seg[k].l==st&&Seg[k].r==ed){ Seg[k].maxval+=val; Seg[k].Lazy+=val; return; } pushDown(k); if(Seg[L(k)].r>=ed){ Modify(st,ed,L(k),val); }else if(Seg[R(k)].l<=st){ Modify(st,ed,R(k),val); }else{ Modify(st,Seg[L(k)].r,L(k),val); Modify(Seg[R(k)].l,ed,R(k),val); } pushUp(k); } int Query(int st,int ed,int k){ if(Seg[k].l==st&&Seg[k].r==ed){ return Seg[k].maxval; } pushDown(k); int res; if(Seg[L(k)].r>=ed){ res=Query(st,ed,L(k)); }else if(Seg[R(k)].l<=st){ res=Query(st,ed,R(k)); }else{ res=max(Query(st,Seg[L(k)].r,L(k)),Query(Seg[R(k)].l,ed,R(k))); } pushUp(k); return res; } int main(){ #ifdef kirito freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif while(~scanf("%d%d",&n,&k)) { memset(pre,0,sizeof(pre)); for(int i=1;i<=n;i++){ scanf("%d",&c[i].val); c[i].r=i; c[i].l=pre[c[i].val]+1; pre[c[i].val]=i; } for(int i=1;i<=n;i++){ dp[i]=dp[i-1]+(c[i].l==1?1:0); } for(int j=2;j<=k;j++){ Build(1,n,1); for(int i=1;i<=n;i++){ Modify(c[i].l,c[i].r,1,1); dp[i]=Query(1,i,1); } } printf("%d ",dp[n]); } return 0; }