状态转移方程: dp[i][j]=max(dp[i-1][k]+sum[j]-sum[k]) 复杂度为k*n*n
但是求最大值这个过程可以用线段树来优化
要查询一个区间有多少种颜色 先处理处每个位置i的左边最近的同色位置在哪 记为left[i] 然后对[left[i]+1,i]这个区间加一 因为再靠左的区间已经有另一个同色发挥作用了 没必要再更新
#include <bits/stdc++.h>
using namespace std;
#define N 0x3f3f3f3f
struct node
{
int l;
int r;
int val;
int laz;
};
node tree[140010];
int dp[100][35010];
int clr[35010],lef[35010],book[35010],pre[35010];
int n,k;
void pushup(int cur)
{
tree[cur].val=max(tree[2*cur].val,tree[2*cur+1].val);
return;
}
void pushdown(int cur)
{
if(tree[cur].laz!=0)
{
tree[2*cur].val+=tree[cur].laz;
tree[2*cur].laz+=tree[cur].laz;
tree[2*cur+1].val+=tree[cur].laz;
tree[2*cur+1].laz+=tree[cur].laz;
tree[cur].laz=0;
}
return;
}
void build(int l,int r,int cur)
{
int m;
tree[cur].l=l;
tree[cur].r=r;
tree[cur].val=0;
tree[cur].laz=0;
if(l==r)
{
tree[cur].val=pre[l];
return;
}
m=(l+r)/2;
build(l,m,2*cur);
build(m+1,r,2*cur+1);
pushup(cur);
return;
}
void update(int pl,int pr,int cur)
{
if(pl<=tree[cur].l&&tree[cur].r<=pr)
{
tree[cur].val++;
tree[cur].laz++;
return;
}
pushdown(cur);
if(pl<=tree[2*cur].r) update(pl,pr,2*cur);
if(pr>=tree[2*cur+1].l) update(pl,pr,2*cur+1);
pushup(cur);
return;
}
int query(int pl,int pr,int cur)
{
int res;
if(pl<=tree[cur].l&&tree[cur].r<=pr)
{
return tree[cur].val;
}
pushdown(cur);
res=-N;
if(pl<=tree[2*cur].r) res=max(res,query(pl,pr,2*cur));
if(pr>=tree[2*cur+1].l) res=max(res,query(pl,pr,2*cur+1));
return res;
}
int main()
{
int i,j;
scanf("%d%d",&n,&k);
for(i=1;i<=n;i++)
{
scanf("%d",&clr[i]);
lef[i]=book[clr[i]]+1,book[clr[i]]=i;
}
for(j=1;j<=n;j++)
{
dp[1][j]=dp[1][j-1];
if(lef[j]==1) dp[1][j]++;
}
for(i=2;i<=k;i++)
{
for(j=1;j<=n;j++)
{
pre[j]=dp[i-1][j-1];
}
build(1,n,1);
for(j=1;j<=n;j++)
{
update(lef[j],j,1);
dp[i][j]=query(1,j,1);
}
}
printf("%d\n",dp[k][n]);
return 0;
}