Codeforces 834D The Bakery (DP + 线段树)

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 

思路:

我们可以想一个动规方程,

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;
}

猜你喜欢

转载自blog.csdn.net/kidsummer/article/details/81542670
今日推荐