题意
将一个长度为\(n\)的序列分为\(k\)段
使得总价值最大 一段区间的价值表示为区间内不同数字的个数\(n<=35000,k<=50\)Sol
首先可以设状态\(f[i][j]\)
表示前\(i\)个数字分成\(j\)段的最大价值 则\(f[i][j] = max\){ \(f[l][j - 1] + Query(l + 1, i)\)}\((0 <= l <= i - 1)\)\(Query(l + 1, i)\)表示\(l+1\)到\(i\)的不同数的个数然后
你会发现 设\(i\)这个位置上的数上次的位置为\(lst\) 则枚举到这个数字后,\(lst\)到\(i-1\)这些\(f\)都要加\(1\) 那么就可以枚举\(k\) 然后每次重构线段树,把上一次分的\(f\)放进去 查询+区间加法# include# define RG register# define IL inline# define Fill(a, b) memset(a, b, sizeof(a))using namespace std;typedef long long ll;const int _(4e4 + 5);IL int Input(){ RG int x = 0, z = 1; RG char c = getchar(); for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1; for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48); return x * z;}int n, k, a[_], mx[_ << 2], tag[_ << 2], f[_], lst[_], id[_];IL void Build(RG int x, RG int l, RG int r){ tag[x] = 0; if(l == r){ mx[x] = f[l]; return; } RG int mid = (l + r) >> 1; Build(x << 1, l, mid), Build(x << 1 | 1, mid + 1, r); mx[x] = max(mx[x << 1], mx[x << 1 | 1]);}IL void Modify(RG int x, RG int l, RG int r, RG int L, RG int R){ if(L <= l && R >= r){ ++mx[x], ++tag[x]; return; } RG int mid = (l + r) >> 1; if(L <= mid) Modify(x << 1, l, mid, L, R); if(R > mid) Modify(x << 1 | 1, mid + 1, r, L, R); mx[x] = max(mx[x << 1], mx[x << 1 | 1]) + tag[x];}IL int Query(RG int x, RG int l, RG int r, RG int L, RG int R){ if(L <= l && R >= r) return mx[x]; RG int mid = (l + r) >> 1, ans = 0; if(L <= mid) ans = Query(x << 1, l, mid, L, R); if(R > mid) ans = max(ans, Query(x << 1 | 1, mid + 1, r, L, R)); return ans + tag[x];}int main(RG int argc, RG char *argv[]){ n = Input(), k = Input(); for(RG int i = 1; i <= n; ++i) a[i] = Input(), lst[i] = id[a[i]], id[a[i]] = i; for(RG int i = 1; i <= k; ++i){ Build(1, 0, n), Fill(f, 0); for(RG int j = i; j <= n; ++j){ Modify(1, 0, n, lst[j], j - 1); f[j] = Query(1, 0, n, 0, j - 1); } } printf("%d\n", f[n]); return 0;}