CF 833B
互测题T3...
首先有个dp是非常好想的:
设dp[i][j]为前j个数分成i组的最大得分,则易得:dp[i][j]=max{dp[i-1][k-1]+num[k][j]},其中,num[k][j]表示从第k个数到第j个数不同值的数量
而num数组可以预处理出来,时间复杂度O(n^2 k)
等等,这样好像过不掉这道题啊
我们发现,max{dp[i-1][k-1]+num[k][j]}这个东西是不是应该用什么维护一下?
线段树!
利用线段树,我们可以实现区间求最值!
我们记录一个位置i上的数a[i]上一次出现的位置为las[i],那么当我们更新dp到位置i时,我们就可以进行转移,而如果一共分了k组,则最后这一组的起点一定在k以后!
同时,利用las[i],我们可以将las[i]+1~i这一整段区间的值全部+1,因为这一段区间在更新dp时不产生重复
最后我们在k~i这段区间上做区间查询,求最大值即为dp值
每次更新完一组的dp值以后,都需要重新建树,类似滚动数组的原理
还有,在建树时,考虑到转移方程中需要用到的是dp[i-1][k-1],所以在建树时赋值的下标都应当-1以便利用
代码:
#include <cstdio> #include <cmath> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #include <queue> #include <stack> #define rt1 rt<<1 #define rt2 (rt<<1)|1 #define ls tree[rt].lson #define rs tree[rt].rson using namespace std; struct Tree { int lson; int rson; int maxval; int lazy; }tree[400005]; int dp[55][35005]; int las[35005]; int p[35005]; int n,k; void buildtree(int l,int r,int rt,int typ) { ls=l; rs=r; tree[rt].lazy=0; if(l==r) { tree[rt].maxval=dp[typ][l-1]; return; } int mid=(l+r)>>1; buildtree(l,mid,rt1,typ); buildtree(mid+1,r,rt2,typ); tree[rt].maxval=max(tree[rt1].maxval,tree[rt2].maxval); } void pushdown(int rt) { int t=tree[rt].lazy; tree[rt].lazy=0; tree[rt1].lazy+=t; tree[rt2].lazy+=t; tree[rt1].maxval+=t; tree[rt2].maxval+=t; } void ins(int l,int r,int v,int rt) { if(ls>r||rs<l) { return; } if(ls>=l&&rs<=r) { tree[rt].lazy+=v; tree[rt].maxval+=v; return; } pushdown(rt); int mid=(ls+rs)>>1; if(l<=mid) { ins(l,r,v,rt1); } if(r>mid) { ins(l,r,v,rt2); } tree[rt].maxval=max(tree[rt1].maxval,tree[rt2].maxval); } int query(int l,int r,int rt) { if(l>rs||r<ls) { return 0; } if(l<=ls&&r>=rs) { return tree[rt].maxval; } pushdown(rt); return max(query(l,r,rt1),query(l,r,rt2)); } int main() { // freopen("handsome.in","r",stdin); // freopen("handsome.out","w",stdout); scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) { int x; scanf("%d",&x); las[i]=p[x]; p[x]=i; } buildtree(1,n,1,0); for(int i=1;i<=k;i++) { for(int j=1;j<=n;j++) { int lq=las[j]; ins(lq+1,j,1,1); dp[i][j]=query(i,j,1); } buildtree(1,n,1,i); } printf("%d\n",dp[k][n]); return 0; }