CF833B The Bakery
题意
将一个长度为\(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 <bits/stdc++.h>
# 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;
}