题目链接
本菜感觉这个题目真是难,看了好久的题解才懂起,大致题意就是给一个数组,把数组连续的分为k部分,每一部分的值等于这部分不同的数字的个数,要让所有的值加起来最大,问怎样分。
首先我们来看一下dp:
dp[i][j]
表示前i个数字分成j组的的最优方案,那么很容易就能够得出一个状态转移公式
dp[i][j]=max(dp[t][j−1]+F(t+1,i),j−1<=t<i)
其中
F(x,y)
表示x到y的分为一个区间的价值,但是复杂度完太高,于是这里用线段树来处理一下,最外层来枚举j,然后枚举i的时候有线段树来优化,怎么弄呢?
首先把线段树用
dp[x][j−1],(0<=x<=n)]
来初始化,那么线段树就是维护的整个区间的最大值,然后更新当前状态的时候,就从上一个和当前数字相等的数位置t 到 i-1区间的值都加1 然后当前最优的值就是0到i-1 区间的最大值,因为每次加一相当于从那个位置往后的区间多一个不同的数也就是当前的第j个区间的值,然后线段树查询就能过,最后答案就是dp[n][k]
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<string>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cstring>
#define clr(x) memset(x,0,sizeof(x))
using namespace std;
#define LL long long
const int N = 35005;
struct TreeNode{
int ma;
int cnt;
}tree[N<<2];
int n;
void PushUp(int now){
tree[now].ma = max(tree[now<<1].ma,tree[now<<1|1].ma);
}
void PushDown(int now){
if(tree[now].cnt!=0)
{
int lson = now<<1;
int rson = now<<1|1;
tree[lson].cnt += tree[now].cnt;
tree[lson].ma += tree[now].cnt;
tree[rson].cnt += tree[now].cnt;
tree[rson].ma += tree[now].cnt;
tree[now].cnt = 0;
}
}
void Update(int ul,int ur,int add,int l,int r,int now)
{
if(ul<=l && r <= ur)
{
tree[now].cnt += add;
tree[now].ma += add;
return;
}
PushDown(now);
int mid = (l+r)/2;
if(ul<=mid)
Update(ul,ur,add,l,mid,now<<1);
if(ur>mid)
Update(ul,ur,add,mid+1,r,now<<1|1);
PushUp(now);
}
int Query(int ql,int qr,int l,int r,int now)
{
if(ql <= l && r <= qr)
{
return tree[now].ma;
}
PushDown(now);
int mid = (l+r)/2;
int a1,a2;
a1 = a2 = -9999999;
if(ql<=mid)
{
a1 = Query(ql,qr,l,mid,now<<1);
}
if(qr>mid)
{
a2 = Query(ql,qr,mid+1,r,now<<1|1);
}
return max(a2,a1);
}
int dat[35005];
int dp[35005][55];
int last[35005];
int now[35005];
int main()
{
int k;
while(scanf("%d%d",&n,&k)!=EOF)
{
clr(last);
clr(now);
for(int i = 1;i<=n;i++)
{
scanf("%d",&dat[i]);
last[i] = now[dat[i]];
now[dat[i]] = i;
}
clr(dp);
clr(tree);
for(int i = 1;i<=k;i++)
{
clr(tree);
for(int j = 1;j<=n;j++)
{
Update(j,j,dp[j][i-1],0,n,1);
}
for(int j = i;j<=n;j++)
{
Update(max(last[j],i-1),j-1,1,0,n,1);
/* if(i==3)
{
for(int ii = 1;ii<=n;ii++)
{
cout << Query(ii,ii,0,n,1) << " ";
}
cout << endl;
}*/
//if(i==3 && j)cout << j<<"kk" << Query(2,7,0,n,1) << endl;
dp[j][i] = Query(i-1,j-1,0,n,1);
}
}
printf("%d\n",dp[n][k]);
}
return 0;
}