题意:
把 n 个数划分成 m 段,要求每组数不相等的数的数量最大之和。
思路:
dp方程 : dp[i][j] = max( dp[k][j-1] + v(k, i) );( j<=k<i , k = j, j+1, +...+ i-1)
dp[i][j]表示第 i 个数分到第 j 段的最大值。
v(k, i) 表示k~i中不同数的个数,此处用hash记录每个数上一次出现的位置,从上一次出现的位置到当前位置的 dp[i][j-1] 值均可+1。
线段树优化:维护 dp[k~i][j-1]的最大值。
此时时间复杂度 O(n*m*log(n))。
代码:
#include<iostream> #include<cstdio> #include<cstring> #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 using namespace std; const int maxn = 35010, maxk = 55, INF = 0x3f3f3f3f; int a[maxn], dp[maxn][maxk], last[maxn], now[maxn]; int maxv[maxn<<2], lazy[maxn<<2]; push_down(int rt) { if(lazy[rt]) { lazy[rt<<1]+=lazy[rt]; lazy[rt<<1|1]+=lazy[rt]; maxv[rt<<1]+=lazy[rt]; maxv[rt<<1|1]+=lazy[rt]; lazy[rt]=0; } } push_up(int rt) { maxv[rt]=max(maxv[rt<<1], maxv[rt<<1|1]); } void update(int l, int r, int rt, int ul, int ur, int v) { if(ul<=l && r<=ur) { maxv[rt]+=v; lazy[rt]+=v; return; } push_down(rt); int m=(l+r)/2; if(ul<=m) update(lson, ul, ur, v); if(m<ur) update(rson, ul, ur, v); push_up(rt); } int query(int l, int r, int rt, int ql, int qr) { if(ql<=l && r<=qr) return maxv[rt]; push_down(rt); int m=(l+r)/2, res=-INF; if(ql<=m) res=max(res, query(lson, ql, qr)); if(qr>m) res=max(res, query(rson, ql, qr)); push_up(rt); return res; } int main() { int n, m; scanf("%d%d", &n, &m); for(int i=1; i<=n; ++i) scanf("%d", &a[i]); for(int i=1; i<=n; ++i) { last[i]=now[a[i]]; now[a[i]]=i; } for(int j=1; j<=m; ++j) { if(j!=1) { memset(maxv, 0, sizeof maxv); memset(lazy, 0, sizeof lazy); for(int i=j-1; i<=n; ++i) { update(0, n, 1, i, i, dp[i][j-1]); } } update(0, n, 1, max(j-1, last[j]), j-1, 1); dp[j][j]=j; for(int i=j+1; i<=n; ++i) { update(0, n, 1, max(j-1, last[i]), i-1, 1); dp[i][j]=query(0, n, 1, j-1, i-1); } } printf("%d ", dp[n][m]); return 0; }