<h2>题目大意</h2>
把 $n$ 个数分成 $k$ 个区间,每个区间的价值为区间内不同数字的个数,问价值和最大为多少。
<h2>题解</h2>
简单 dp:设 $f_{i,j}$ 表示前 $i$ 个数 分了 $j$ 段的价值和最大是多少。我们记 $cnt_{l,r}$ 表示 $[l,r]$ 的不同的数的数量,那么有转移
$$f_{i,j} = \min {f_{k,j-1}+cnt_{k+1,i}}$$
朴素的转移是 $O(n^3)$ 的,我们观察这个式子,如果固定 $j$ ,我们用线段树维护 $f_k+cnt_{k+1,i}$ 就可以达到 $O(logn)$ 的转移了。当 $i \to i+1$ 时我们只需要修改一下线段树 更新一下 $cnt$ 就可以了。
/*
* Author: RainAir
* Time: 2019-08-23 15:03:54
*/
#include <algorithm>
#include <iostream>
#include <cstring>
#include <climits>
#include <cstdlib>
#include <cstdio>
#include <bitset>
#include <vector>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <map>
#include <set>
#define fi first
#define se second
#define U unsigned
#define P std::pair
#define Re register
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(Re int i = a;i <= b;++i)
#define ROF(i,a,b) for(Re int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl
const int MAXN = 5e4 + 5;
int mx[MAXN<<2],tag[MAXN<<2];
int f[MAXN],g[MAXN];
#define lc ((x)<<1)
#define rc ((x)<<1|1)
inline void clear(){
CLR(mx,0);CLR(tag,0);CLR(f,0);
}
inline void pushup(int x){
mx[x] = std::max(mx[lc],mx[rc]);
}
inline void cover(int x,int l,int r,int d){
mx[x] += d;tag[x] += d;
}
inline void pushdown(int x,int l,int r){
if(tag[x]){
int mid = (l + r) >> 1;
cover(lc,l,mid,tag[x]);
cover(rc,mid+1,r,tag[x]);
tag[x] = 0;
}
}
inline void build(int x,int l,int r){
if(l == r){
mx[x] = g[l-1];
return;
}
int mid = (l + r) >> 1;
build(lc,l,mid);build(rc,mid+1,r);
pushup(x);
}
inline void modify(int x,int l,int r,int L,int R,int d){
if(L > R) return;
if(l == L && r == R){
cover(x,l,r,d);return;
}
int mid = (l + r) >> 1;
pushdown(x,l,r);
if(R <= mid) modify(lc,l,mid,L,R,d);
else if(L > mid) modify(rc,mid+1,r,L,R,d);
else modify(lc,l,mid,L,mid,d),modify(rc,mid+1,r,mid+1,R,d);
pushup(x);
}
inline int query(int x,int l,int r,int L,int R){
if(l == L && r == R) return mx[x];
int mid = (l + r) >> 1;pushdown(x,l,r);
if(R <= mid) return query(lc,l,mid,L,R);
if(L > mid) return query(rc,mid+1,r,L,R);
return std::max(query(lc,l,mid,L,mid),query(rc,mid+1,r,mid+1,R));
pushup(x);
}
int n,k,a[MAXN],pre[MAXN],lst[MAXN];
int main(){
scanf("%d%d",&n,&k);
FOR(i,1,n) scanf("%d",a+i);
FOR(i,1,n) pre[i] = lst[a[i]],lst[a[i]] = i;
FOR(i,1,k){
build(1,1,n);
FOR(j,1,n){
modify(1,1,n,pre[j]+1,j,1);
f[j] = query(1,1,n,1,j);
}
FOR(j,1,n) g[j] = f[j];
clear();
}
printf("%dn",g[n]);
return 0;
}