链接:
http://codeforces.com/contest/833/problem/B
题意:
给你n个数,分成k段,每段的值为其中不同的数的个数,求每段的值的和的最大值
题解:
显然需要用到动态规划。我们用dp[i][j]表示前j个数分成i段价值和的最大值。容易得到状态转移方程为
dp[i][j]=max{dp[i-1][x]+剩下数的贡献} {1<=x< j}
过程用线段树维护一下区间最大值即可。
代码:
31 int n, k; 32 int a[MAXN]; 33 int pos[MAXN], pre[MAXN]; 34 int Tree[MAXN << 2], Lazy[MAXN << 2]; 35 int dp[60][MAXN]; 36 37 void pushup(int rt) { 38 Tree[rt] = max(Tree[rt << 1], Tree[rt << 1 | 1]); 39 } 40 41 void pushdown(int rt) { 42 if (Lazy[rt]) { 43 Tree[rt << 1] += Lazy[rt]; 44 Tree[rt << 1 | 1] += Lazy[rt]; 45 Lazy[rt << 1] += Lazy[rt]; 46 Lazy[rt << 1 | 1] += Lazy[rt]; 47 Lazy[rt] = 0; 48 } 49 } 50 51 void build(int pos, int l, int r, int rt) { 52 Lazy[rt] = 0; 53 if (l == r) { 54 Tree[rt] = dp[pos][l - 1]; 55 return; 56 } 57 int m = (l + r) >> 1; 58 build(pos, lson); 59 build(pos, rson); 60 pushup(rt); 61 } 62 63 void update(int L, int R, int l, int r, int rt) { 64 if (L <= l && r <= R) { 65 Tree[rt]++; 66 Lazy[rt]++; 67 return; 68 } 69 pushdown(rt); 70 int m = (l + r) >> 1; 71 if (L <= m) update(L, R, lson); 72 if (R > m) update(L, R, rson); 73 pushup(rt); 74 } 75 76 int query(int L, int R, int l, int r, int rt) { 77 if (L <= l && r <= R) return Tree[rt]; 78 pushdown(rt); 79 int m = (l + r) >> 1; 80 int ret = 0; 81 if (L <= m) ret = max(ret, query(L, R, lson)); 82 if (R > m) ret = max(ret, query(L, R, rson)); 83 pushup(rt); 84 return ret; 85 } 86 87 int main() { 88 ios::sync_with_stdio(false), cin.tie(0); 89 cin >> n >> k; 90 rep(i, 1, n + 1) { 91 cin >> a[i]; 92 pre[i] = pos[a[i]] + 1; 93 pos[a[i]] = i; 94 } 95 rep(i, 1, k + 1) { 96 build(i - 1, 1, n, 1); 97 rep(j, 1, n + 1) { 98 update(pre[j], j, 1, n, 1); 99 dp[i][j] = query(1, j, 1, n, 1); 100 } 101 } 102 cout << dp[k][n] << endl; 103 return 0; 104 }