Codeforces 833B
题意:把n个数分成k部分,使得每部分价值之和最大。每部分的价值为不同数的个数
我们首先知道这题很容易用 d p [ i ] [ k ]    想到是以 i    结尾 前面已经有 k 部分的方案数 那么 d p [ i ] [ k ]    =    max ( d p [ i ] [ k − 1 ] , d p [ j ] [ k − 1 ] + n u m [ j + 1 ] [ i ] ) 这样直接跑是 O ( n 2 m ) 的 会 T L E    如果我们把取 max x    用一个数组记录下来 那么每次决策就是 O ( 1 ) 的转移 但是这不是类似层次的东西 所以我们可以建一颗线段树 叶子节点的值是 d p [ j ] [ k − 1 ]    +    n u m [ j + 1 ] [ i ] 可是怎么更新 n u m 呢?对于这种不同数的问题 我们很容易想到 p r e 数组 我们不考虑单个的 n u m 我们考虑全局 n u m    每个数字对一段区间的贡献就是 p r e [ i ] , i − 1 这段会加 1 于是更新部分就结束了 就可以开始 q u e r y    了 因为 k − 1 < = j    <    i    所以 j 范围也有了 i 每次要从 k 开始 不然前面不能凑齐 k − 1个 \text{我们首先知道这题很容易用}dp\left[ i \right] \left[ k \right] \,\,\text{想到是以 }i\,\,\text{结尾 前面已经有}k\text{部分的方案数} \\ \text{那么}dp\left[ i \right] \left[ k \right] \,\,=\,\,\max \left( dp\left[ i \right] \left[ k-1 \right] ,dp\left[ j \right] \left[ k-1 \right] +num\left[ j+1 \right] \left[ i \right] \right) \\ \text{这样直接跑是}O\left( n^2m \right) \text{的 会}TLE\,\,\text{如果我们把取}\max x\,\,\text{用一个数组记录下来} \\ \text{那么每次决策就是}O\left( 1 \right) \text{的转移 但是这不是类似层次的东西} \\ \text{所以我们可以建一颗线段树} \\ \text{叶子节点的值是 }dp\left[ j \right] \left[ k-1 \right] \,\,+\,\,num\left[ j+1 \right] \left[ i \right] \\ \text{可是怎么更新}num\text{呢?对于这种不同数的问题} \\ \text{我们很容易想到}pre\text{数组 我们不考虑单个的}num \\ \text{我们考虑全局}num\,\,\text{每个数字对一段区间的贡献就是 }pre\left[ i \right] ,i-\text{1 这段会加}1 \\ \text{于是更新部分就结束了 就可以开始}query\,\,\text{了 因为 }k-1<=j\,\,<\,\,i\,\,\text{所以}j\text{范围也有了} \\ i\text{每次要从}k\text{开始 不然前面不能凑齐}k-\text{1个} \\ 我们首先知道这题很容易用dp[i][k]想到是以 i结尾 前面已经有k部分的方案数那么dp[i][k]=max(dp[i][k−1],dp[j][k−1]+num[j+1][i])这样直接跑是O(n2m)的 会TLE如果我们把取maxx用一个数组记录下来那么每次决策就是O(1)的转移 但是这不是类似层次的东西所以我们可以建一颗线段树叶子节点的值是 dp[j][k−1]+num[j+1][i]可是怎么更新num呢?对于这种不同数的问题我们很容易想到pre数组 我们不考虑单个的num我们考虑全局num每个数字对一段区间的贡献就是 pre[i],i−1 这段会加1于是更新部分就结束了 就可以开始query了 因为 k−1<=j<i所以j范围也有了i每次要从k开始 不然前面不能凑齐k−1个
/*
if you can't see the repay
Why not just work step by step
rubbish is relaxed
to ljq
*/
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <cmath>
#include <map>
#include <stack>
#include <set>
#include <sstream>
#include <vector>
#include <stdlib.h>
#include <algorithm>
using namespace std;
#define dbg(x) cout<<#x<<" = "<< (x)<< endl
#define dbg2(x1,x2) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<endl
#define dbg3(x1,x2,x3) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<" "<<#x3<<" = "<<x3<<endl
#define max3(a,b,c) max(a,max(b,c))
#define min3(a,b,c) min(a,min(b,c))
#define lc (rt<<1)
#define rc (rt<<11)
#define mid ((l+r)>>1)
typedef pair<int,int> pll;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int _inf = 0xc0c0c0c0;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll _INF = 0xc0c0c0c0c0c0c0c0;
const ll mod = (int)1e9+7;
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll ksm(ll a,ll b,ll mod){int ans=1;while(b){if(b&1) ans=(ans*a)%mod;a=(a*a)%mod;b>>=1;}return ans;}
ll inv2(ll a,ll mod){return ksm(a,mod-2,mod);}
void exgcd(ll a,ll b,ll &x,ll &y,ll &d){if(!b) {d = a;x = 1;y=0;}else{exgcd(b,a%b,y,x,d);y-=x*(a/b);}}//printf("%lld*a + %lld*b = %lld\n", x, y, d);
const int MAX_N = 35025;
int maxx[MAX_N<<2],col[MAX_N<<2],pre[MAX_N],last[MAX_N],arr[MAX_N],dp[MAX_N][55];
set<int > st;
void up(int rt)
{
maxx[rt] = max(maxx[rt<<1],maxx[rt<<1|1]);
}
void down(int rt,int l,int r)
{
if(col[rt])
{
col[rt<<1] += col[rt];
col[rt<<1|1] += col[rt];
maxx[rt<<1] += col[rt];
maxx[rt<<1|1] += col[rt];
col[rt] = 0;
}
}
void build(int rt,int l,int r,int v)
{
col[rt] = maxx[rt] = 0;
if(l==r)
{
maxx[rt] = dp[l][v];
return;
}
build(rt<<1,l,mid,v);
build(rt<<1|1,mid+1,r,v);
up(rt);
}
void update(int rt,int l,int r,int x,int y)
{
if(x<=l&&r<=y)
{
col[rt]++;
maxx[rt]++;
return;
}
down(rt,l,r);
if(x<=mid) update(rt<<1,l,mid,x,y);
if(mid<y) update(rt<<1|1,mid+1,r,x,y);
up(rt);
}
int query(int rt,int l,int r,int x,int y)
{
if(x<=l&&r<=y)
{
return maxx[rt];
}
down(rt,l,r);
if(y<=mid) return query(rt<<1,l,mid,x,y);
else if(x>mid) return query(rt<<1|1,mid+1,r,x,y);
else return max(query(rt<<1,l,mid,x,y),query(rt<<1|1,mid+1,r,x,y));
}
int main()
{
//ios::sync_with_stdio(false);
//freopen("a.txt","r",stdin);
//freopen("b.txt","w",stdout);
int n,m;
scanf("%d%d",&n,&m);
for(int i = 1;i<=n;++i) scanf("%d",&arr[i]),pre[i] = last[arr[i]],last[arr[i]] = i,st.insert(arr[i]),dp[i][1] = st.size();
for(int k = 2;k<=m;++k)
{
build(1,1,n,k-1);
for(int i = k;i<=n;++i)
{
update(1,1,n,pre[i],i-1);
dp[i][k] = max(dp[i][k-1],query(1,1,n,k-1,i-1));
}
}
printf("%d\n",dp[n][m]);
//fclose(stdin);
//fclose(stdout);
//cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;
return 0;
}