题目大意:给出n个数的数列,其中保证1<=ai<=n,使其分成m份,使得每一段字符集大小相加最大,求最大值。
考场上跪了,回来一想发现是线段树维护dp:
原dp:dp[ i ][ j ] = max ( dp[ j ][ k-1 ] + num[ j+1 ][ i ]);
然后发现线段树可以维护,即将等号右边的那一段扔到线段树中,时间复杂度为 O( nlogn )。
代码:
#include<cstdio> #include<algorithm> using namespace std; #define N 35050 int n,K; int a[N],las[N],nxt[N]; int v[N<<2],mrk[N<<2]; int dp[51][N]; void update(int u) { v[u]=max(v[u<<1],v[u<<1|1]); } void add(int u,int d) { v[u]+=d; mrk[u]+=d; } void pushdown(int u) { if(mrk[u]) { add(u<<1,mrk[u]); add(u<<1|1,mrk[u]); mrk[u]=0; } } void build(int l,int r,int u,int k) { mrk[u]=0; if(l==r) { v[u]=dp[k][l-1]; return ; } int mid = (l+r)>>1; build(l,mid,u<<1,k); build(mid+1,r,u<<1|1,k); update(u); } void insert(int l,int r,int u,int ql,int qr) { if(l==ql&&r==qr) { add(u,1); return ; } pushdown(u); int mid = (l+r)>>1; if(ql>mid)insert(mid+1,r,u<<1|1,ql,qr); else if(qr<=mid)insert(l,mid,u<<1,ql,qr); else insert(l,mid,u<<1,ql,mid),insert(mid+1,r,u<<1|1,mid+1,qr); update(u); } int query(int l,int r,int u,int ql,int qr) { if(l==ql&&r==qr)return v[u]; pushdown(u); int mid = (l+r)>>1; if(ql>mid)return query(mid+1,r,u<<1|1,ql,qr); else if(qr<=mid)return query(l,mid,u<<1,ql,qr); else return max(query(l,mid,u<<1,ql,mid),query(mid+1,r,u<<1|1,mid+1,qr)); } int main() { // freopen("handsome.in","r",stdin); // freopen("handsome.out","w",stdout); scanf("%d%d",&n,&K); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); nxt[i]=las[a[i]]; las[a[i]]=i; } for(int k=1;k<=K;k++) { build(1,n,1,k-1); for(int i=1;i<=n;i++) { insert(1,n,1,nxt[i]+1,i); dp[k][i]=query(1,n,1,1,i); } } printf("%d ",dp[K][n]); return 0; }