题面

传送门:http://codeforces.com/problemset/problem/833/B
B. The Bakery
time limit per test2.5 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output

Some time ago Slastyona the Sweetmaid decided to open her own bakery! She bought required ingredients and a wonder-oven which can bake several types of cakes, and opened the bakery.

Soon the expenses started to overcome the income, so Slastyona decided to study the sweets market. She learned it’s profitable to pack cakes in boxes, and that the more distinct cake types a box contains (let’s denote this number as the value of the box), the higher price it has.

She needs to change the production technology! The problem is that the oven chooses the cake types on its own and Slastyona can’t affect it. However, she knows the types and order of n cakes the oven is going to bake today. Slastyona has to pack exactly k boxes with cakes today, and she has to put in each box several (at least one) cakes the oven produced one right after another (in other words, she has to put in a box a continuous segment of cakes).

Slastyona wants to maximize the total value of all boxes with cakes. Help her determine this maximum possible total value.

Input
The first line contains two integers n and k (1 ≤ n ≤ 35000, 1 ≤ k ≤ min(n, 50)) – the number of cakes and the number of boxes, respectively.

The second line contains n integers a1, a2, …, an (1 ≤ ai ≤ n) – the types of cakes in the order the oven bakes them.

Output
Print the only integer – the maximum total value of all boxes with cakes.

Examples
inputCopy
4 1
1 2 2 1
outputCopy
2
inputCopy
7 2
1 3 3 1 4 4 4
outputCopy
5
inputCopy
8 3
7 7 8 7 7 8 1 7
outputCopy
6
Note
In the first example Slastyona has only one box. She has to put all cakes in it, so that there are two types of cakes in the box, so the value is equal to 2.

In the second example it is profitable to put the first two cakes in the first box, and all the rest in the second. There are two distinct types in the first box, and three in the second box then, so the total value is 5.

题目大意:
有一个长度为n的序列,将它分为k段,定义每段的值为该段中不重复元素的个数,求k段值之和的最大值
如:n=4,k=2
1 2 2 1
可分为1,2和2,1两段,值之和为4

分析

1.DP方程的推导

设dp[i][j]dp[i][j]表示前j个数分为i段的最大值
状态转移方程为:
dp[i][j]=max(dp[i][j],dp[i−1][x−1]+num[x][j])dp[i][j]=max(dp[i][j],dp[i−1][x−1]+num[x][j])
其中x∈[i,j]x∈[i,j], num[x][j]num[x][j]表示区间[x,j][x,j]中不同的数的个数
dp本身的时间复杂度为O(n2k)O(n2k),遍历数组求num的时间复杂度为O(n)O(n),总时间复杂度为O(n3k)O(n3k)

2.求不同数个数的优化

设上一个等于a[i]的数的位置为last[i](如果是第一次,则last[i]=0)last[i](如果是第一次,则last[i]=0)
那么
处理到a[j]时,设x从last[j]+1到j,从last[i]+1到j的num[x][j]值均要加1
这是因为从last[i]+1到j的数一定没有a[j],加入a[j]后,不同的数个数一定多了1个
如:
a: 1 2 2 1
last: 0 0 2 1
如果不明白的话,可以在纸上模拟一下过程,但要确保理解上面这句话

3.线段树的优化

我们将状态转移方程中的dp[i−1][x−1]+num[x][j]dp[i−1][x−1]+num[x][j]看作一个整体,发现可以用线段树维护[i,j]最大值,这样只要一次查询就能求出max
另外,从last[i]+1到i的num[x][i]值均要加1这一操作,可以用区间修改来实现
所以我们只要枚举i从1到k,
每次对上一次的dp值(dp[i-1])建树
再枚举j从i到n,用区间修改,然后查询就能求出dp[i][j]的值
建树复杂度O(nlog2n)O(nlog2n)
n次修改和查询时间复杂度为O(nlog2n)O(nlog2n)
总时间复杂度为O(nklog2n)O(nklog2n)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 35005
#define maxk 55
using namespace std;
int n,k;
int a[maxn];
int hash[maxn],last[maxn];
int dp[maxk][maxn];     //dp[i][j]表示前j个数分成i段的方法数
struct node{int l;int r;int v;int mark;
}tree[maxn<<2];
void push_up(int pos){tree[pos].v=max(tree[pos<<1].v,tree[pos<<1|1].v);
}
void build(int i,int l,int r,int pos){tree[pos].l=l;tree[pos].r=r;tree[pos].mark=0;if(l==r){tree[pos].v=dp[i][l-1];return;}int mid=(l+r)>>1;build(i,l,mid,pos<<1);build(i,mid+1,r,pos<<1|1);push_up(pos);
}
void push_down(int pos){if(tree[pos].mark!=0){tree[pos<<1].mark+=tree[pos].mark;tree[pos<<1|1].mark+=tree[pos].mark;tree[pos<<1].v+=tree[pos].mark;tree[pos<<1|1].v+=tree[pos].mark;tree[pos].mark=0;}
}
void update(int L,int R,int v,int pos){if(L<=tree[pos].l&&R>=tree[pos].r){tree[pos].v+=v;tree[pos].mark+=v;return;}push_down(pos);int mid=(tree[pos].l+tree[pos].r)>>1;if(L<=mid)update(L,R,v,pos<<1);if(R>mid)update(L,R,v,pos<<1|1);push_up(pos);
}
int query(int L,int R,int pos){if(L<=tree[pos].l&&R>=tree[pos].r){return tree[pos].v;}push_down(pos);int mid=(tree[pos].l+tree[pos].r)>>1;int ans=0;if(L<=mid)ans=max(ans,query(L,R,pos<<1));if(R>mid) ans=max(ans,query(L,R,pos<<1|1));return ans;
}
int main(){scanf("%d %d",&n,&k);for(int i=1;i<=n;i++){scanf("%d",&a[i]);last[i]=hash[a[i]];//last[i]->a[i]上一次出现的位置 hash[a[i]]=i;//hash[x]-> x上一次出现的位置 }//dp[i][j]=max(dp[i-1][x-1]+num[x][j],dp[i][j]) (i<=x<=j,num[x][j]->[x,j]不同数的个数//线段树维护 dp[i-1][x-1]+num[x][j],变成区间查询最大值问题//加入a[j],从last[j]+1到j的所有num值都要+1 ,用线段树区间更新 for(int i=1;i<=k;i++){build(i-1,1,n,1);for(int j=i;j<=n;j++){update(last[j]+1,j,1,1);dp[i][j]=query(i,j,1);} }printf("%d\n",dp[k][n]);
}

转载于:https://www.cnblogs.com/birchtree/p/9845845.html

Codeforces 833B 题解(DP+线段树)相关推荐

  1. hdu5489 Removed Interval dp+线段树优化

    现在看这题居然直接秒了...去年看的时候还以为神题.. 设以第i项为结尾的lis前缀为f[i],以第j项为结尾的lis后缀为g[i],如果求出f[i]和g[j],然后枚举i,快速找到最大的满足a[j] ...

  2. Codeforces Round #699 (Div. 2) E.Sorting Books(贪心+DP / 线段树)超高质量题解,看不懂来打我 ~

    整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 E - Sorting Books 一排书架上有 nnn 本书排成一排,每本书上有一个颜色 aia_i ...

  3. CodeForces - 1527E Partition Game(dp+线段树)

    题目链接:点击查看 题目大意:给出一个长度为 nnn 的数列,现在需要将其划分成 kkk 段,使得贡献和最小 对于每段区间 [l,r][l,r][l,r] 的贡献为,其中每个数字,其最后一次出现的位置 ...

  4. Codeforces Round #620 (Div. 2) F2. Animal Observation (hard version) dp + 线段树

    传送门 文章目录 题意: 思路: 题意: 比如下面这个图: 思路: 对于这个题,比较容易就能考虑到dpdpdp,设f[i][j]f[i][j]f[i][j]为到了第iii行,覆盖了[j,j+k−1][ ...

  5. Codeforces 671D. Roads in Yusland(树形DP+线段树)

    调了半天居然还能是线段树写错了,药丸 这题大概是类似一个树形DP的东西.设$dp[i]$为修完i这棵子树的最小代价,假设当前点为$x$,但是转移的时候我们不知道子节点到底有没有一条越过$x$的路.如果 ...

  6. Codeforces 213E Two Permutations 线段树 (看题解)

    Two Permutations 关键是没想到按大小顺序把第二个排列一个一个加入线段树, 然后线段树维护整体的hash值, 得到的hs值减去一个sub 之后与, 第一个排列的hash值比较. #inc ...

  7. BZOJ 1852 [MexicoOI06]最长不下降序列(贪心+DP+线段树+离散化)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1852 [题目大意] 给你N对数A1,B1--An,Bn.要求你从中找出最多的对, 把它 ...

  8. Codeforces 997E Good Subsegments (线段树)

    题目链接 https://codeforces.com/contest/997/problem/E 题解 经典题,鸽了 159 天终于看明白题解了.. 考虑一个区间是连续的等价于这个区间内的 \((\ ...

  9. [动态dp]线段树维护转移矩阵

    背景:czy上课讲了新知识,从未见到过,总结一下. 所谓动态dp,是在动态规划的基础上,需要维护一些修改操作的算法. 这类题目分为如下三个步骤:(都是对于常系数齐次递推问题) 1先不考虑修改,不考虑区 ...

最新文章

  1. iframe 有那些缺
  2. CentOS中安装WiFi图形管理工具
  3. linux如何自动清buff,centos7
  4. 【❤️算法系列之二叉树的实现(包含前序、中序、后序遍历以及节点的查找和删除)❤️】
  5. 固态+机械(uefi类型的bios),用easybcd安装win10+ubuntu16.04双系统
  6. 使用TensorFlow 2.0+和Keras实现AlexNet CNN架构
  7. [Ext JS] 3.5 单选框 Radio与复选框CheckBox
  8. C#调用C语言生成的DLL“未找到入口”
  9. 【莫队算法】URAL - 2080 - Wallet
  10. 学习总结-《父与子的编程之旅》chapter 9
  11. 【源码分享】短信平台插件74cms_v4.1_骑士人才系统
  12. 多年珍藏的Android开发必备网站和工具
  13. 用户空间和内核空间的区别
  14. 基于python的RGB图像转灰度图
  15. 科研论文翻译软件|无广告|强力推荐
  16. Kinect坐标转换
  17. NetSuite 负库存控制
  18. KMS激活错误解决方法
  19. 【C语言程序】编写函数返回体温值
  20. 结构光系统标定(四)基于双目视觉的结构光系统标定

热门文章

  1. struts2 hibernate登录
  2. App.Config详解
  3. 京东面试题:二叉树直径
  4. 穷举法--百钱买百鸡
  5. java源码-AQS机制
  6. mybaits二十一:2缓存介绍
  7. 第十三章:位图(三)
  8. Druid 连接池 JDBCUtils 工具类的使用
  9. Java线程:线程的同步与锁
  10. 技术评析:云计算与摩尔定律相悖?