题意:
题目链接:http://codeforces.com/contest/834/problem/D
给出n个数,分割成k个区间,每个区间的价值为该区间内不同数字的种类数,问分割之后,所有区间最大价值和是多少。
思路:
很容易得到方程:
dp[i][j]=max(dp[i-1][k]+kind(k+1,j)
其中dp[i][j]表示的是到第j个数为止已经分了i组的最大价值,k的变化范围是[i,j](想想就能明白),这方程要直接计算复杂度肯定不够。
这里需要用到线段树来优化,思考之后可以发现,当遍历到a[j]的时候,若a[j]上一次出现的位置是last[a[j]],那么a[j]会给起点为[last[a[j]] + 1,j],终点为j的每个区间都增加1的贡献,这一步可以利用线段树区间更新,若线段树上初始存的是i-1轮的dp结果,然后从左到右遍历的过程中每次将新的贡献更新到线段树上,就可以维护max(dp[i-1][k]+kind(k+1,j)
,直接query(i,j)即可得到dp[i][j]。
需要注意的地方是:每次更新贡献区间是[last[a[j]] + 1,j],但这贡献是属于last[a[j]到j-1的dp值,所以要更新的是[last[a[j]],j-1]。并且为了不让线段树区间从0开始,这里可以把last初值设置成1,对结果没有影响。
代码:
#include <bits/stdc++.h>
using namespace std;
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
const int MAXN = 35005;
int a[MAXN], dp[55][MAXN], last[MAXN];
int maxx[MAXN << 2], lazy[MAXN << 2];
void pushup(int rt) {
maxx[rt] = max(maxx[rt << 1], maxx[rt << 1 | 1]);
}
void pushdown(int rt, int len) {
if (lazy[rt]) {
lazy[rt << 1] += lazy[rt];
lazy[rt << 1 | 1] += lazy[rt];
maxx[rt << 1] += lazy[rt];
maxx[rt << 1 | 1] += lazy[rt];
lazy[rt] = 0;
}
}
void update(int s, int e, int z, int l, int r, int rt) {
if (s <= l && r <= e) {
maxx[rt] += z;
lazy[rt] += z;
return;
}
pushdown(rt, r - l + 1);
int m = (l + r) >> 1;
if (s <= m) update(s, e, z, lson);
if (e > m) update(s, e, z, rson);
pushup(rt);
}
int query(int s, int e, int l, int r, int rt) {
if (s <= l && r <= e)
return maxx[rt];
pushdown(rt, r - l + 1);
int m = (l + r) >> 1;
int ret = 0;
if (s <= m) ret = max(ret, query(s, e, lson));
if (e > m) ret = max(ret, query(s, e, rson));
return ret;
}
bool vis[MAXN];
int main() {
// freopen("in.txt", "r", stdin);
int n, k;
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
int kind = 0;
memset(vis, false, sizeof(vis));
for (int i = 1; i <= n; i++) {
if (!vis[a[i]]) ++kind;
dp[1][i] = kind;
vis[a[i]] = true;
}
for (int i = 2; i <= k; i++) {
memset(lazy, 0, sizeof(lazy));
memset(maxx, 0, sizeof(maxx));
for (int j = 1; j <= n; j++) {
update(j, j, dp[i - 1][j], 1, n, 1);
last[a[j]] = 1;
}
for (int j = 1; j < i; j++) last[a[j]] = j;
for (int j = i; j <= n; j++) {
//printf("(%d, %d)\n", last[a[j]] + 1, j);
update(last[a[j]], j - 1, 1, 1, n, 1);
dp[i][j] = query(i - 1, j - 1, 1, n, 1);
last[a[j]] = j;
}
}
printf("%d\n", dp[k][n]);
return 0;
}