Description
给出一个长度为n的序列,把其分成k段连续的子段使得这k段每段中不同数字的个数之和最大
Input
第一行两个整数n和k表示序列长度和要分成的段数,之后n个整数a[i]表示该序列(1<=n<=35000,1<=k<=min(n,50),1<=a[i]<=n)
Output
输出分成k段后每段中不同数字个数之和的最大值
Sample Input
4 1
1 2 2 1
Sample Output
2
思路:
我们可以想一个动规方程,
dp[i][j] = max(dp[i][j], dp[k][j-1] + num[k+1][i]);
dp[i][j] 代表,前 i 个数,分成 k 份,最大值是多少。
num[i][j] 代表 从 i 到 j 这段中不同的数的个数是多少。
然后,我们用线段树维护 dp[k][j-1] + num[k+1][i]
我们可以吧 dp 降成一维,用滚动数组。
因为dp[i][j] 和 dp[i][j-1] 有关系。我们事先吧 dp[i][j-1] 存入线段树中就可以了。
先存入上一个dp 的值,然后我们在不断的更新 num 的值。
当我们找 第 i 个数的时候,我们要事先预处理出来 pre[i] 的值,就是前一个 a[i] 出现的位置。
然后我们在 pre[i] i-1 这个区间内加 1.
为什么要加一 呢。看下面的图。
相当于我们在这个区间内切一刀,分成两个区间,然后一样的数分在了不同的区间,这时候总的价值肯定要加一。
k 是属于 pre[i] i-1 的,因为我们是在 k 的后面切一刀。分成两个区间。
#include <bits/stdc++.h>
#define mem(x,v) memset(x,v,sizeof(x))
#define rep(i,a,b) for (int i = a; i < b; i++)
#define per(i,a,b) for (int i = a; i > b; i--)
using namespace std;
typedef long long LL;
const double EPS = 1e-10;
const int INF = 0x3f3f3f3f;
const int N = 1e5+10;
const int M = 1e5+10;
struct segtree{
int l,r,a,b,w,c;
}f[N];
int last[N],pre[N];
int n,m,t,dp[N],a[N];
void build(int p, int a, int b){
f[p].a = a; f[p].b = b; f[p].c = 0;
if (a + 1 == b){
f[p].w = dp[a];
return;
}
int m = (a + b)/2;
t++; f[p].l = t; build(t,a,m);
t++; f[p].r = t; build(t,m,b);
f[p].w = max(f[f[p].l].w,f[f[p].r].w);
return;
}
void push_down(int p){
if (f[p].c == 0) return;
f[f[p].l].w += f[p].c;
f[f[p].r].w += f[p].c;
f[f[p].l].c += f[p].c;
f[f[p].r].c += f[p].c;
f[p].c = 0;
return;
}
void Insert(int p, int x, int y){
if (x > y) return;
if (x <= f[p].a && y >= f[p].b-1){
f[p].w++; f[p].c++;
return;
}
push_down(p);
int m = (f[p].a + f[p].b) / 2;
if (x < m) Insert(f[p].l,x,y);
if (y >= m) Insert(f[p].r,x,y);
f[p].w = max(f[f[p].l].w, f[f[p].r].w);
return;
}
int Qurey(int p, int x, int y){
int ans = 0;
if (x <= f[p].a && y >= f[p].b-1){
return f[p].w;
}
int m = (f[p].a + f[p].b)/2;
push_down(p);
if (x < m) ans = max(ans,Qurey(f[p].l,x,y));
if (y >= m) ans = max(ans, Qurey(f[p].r,x,y));
return ans;
}
int main(){
scanf("%d%d",&n,&m);
mem(last,0);
rep(i,1,n+1){
scanf("%d",&a[i]);
pre[i] = last[a[i]];
last[a[i]] = i;
}
mem(dp,0);
rep(k,0,m){
t = 1;
build(1,0,n+1);
rep(i,1,n+1){
Insert(1,pre[i],i-1);
dp[i] = Qurey(1,0,i-1);
}
}
printf("%d\n",dp[n]);
return 0;
}