B. The Bakery

time limit per test

2.5 seconds

memory limit per test

256 megabytes

input

standard input

output

standard 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

Input

4 1
1 2 2 1

Output

2

Input

7 2
1 3 3 1 4 4 4

Output

5

Input

8 3
7 7 8 7 7 8 1 7

Output

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.

题意:

给你一个数字串,将串分为k段,每段的值为不同数字的种类数,求所有段的值之和的最大值。

思路:

用DP来解,DP[i][j] 表示的是前i个分为j段可以获得的最大值

状态转移方程为:

DP[i][j]= { DP[k][j-1] + val[k+1][i] ,  j-1<=k<i }

val[k+1][i] 代表的是k+1到i段不同种类的个数

我们可以用线段树来快速求出这个val值,详见我的另一篇博客:http://www.cnblogs.com/liuzhanshan/p/7296128.html

如果按照这个状态转移方程不进行优化来解的话,需要三重循环,O(n^3)显然是不能接受的。

我们利用滚动数组的思想进行优化,

首先枚举分段数量(外层循环为j,内层循环为i)。DP[i]为进行到当前j时,dp[k][j] {j-1<=k<i} 的最大值

同时,利用线段树来维护当前DP[i],具体操作为每次进行内循环之前,重建一次线段树,将i点更新为dp[i-1](这里需要一个偏移量,具体原因见如何求val值及代码)

代码:

  1 /*
2 * @FileName: D:\代码与算法\2017训练比赛\CF#426\b-pro.cpp
3 * @Author: Pic
4 * @Date: 2017-08-16 19:51:14
5 * @Last Modified time: 2017-08-16 21:57:55
6 */
7
8 #include<bits/stdc++.h>
9 using namespace std;
10 const int INFINITE = INT_MAX;
11 const int MAXN=35000+30;
12 const int MAXNUM = MAXN*4+30;
13 struct SegTreeNode
14 {
15 int val;
16 int addMark;//延迟标记
17 }segTree[MAXNUM];//定义线段树
18
19 /*
20 功能:当前节点的标志域向孩子节点传递
21 root: 当前线段树的根节点下标
22 */
23 void pushDown(int root)
24 {
25 if(segTree[root].addMark != 0)
26 {
27 //设置左右孩子节点的标志域,因为孩子节点可能被多次延迟标记又没有向下传递
28 //所以是 “+=”
29 segTree[root*2+1].addMark += segTree[root].addMark;
30 segTree[root*2+2].addMark += segTree[root].addMark;
31 //根据标志域设置孩子节点的值。因为我们是求区间最小值,因此当区间内每个元
32 //素加上一个值时,区间的最小值也加上这个值
33 segTree[root*2+1].val += segTree[root].addMark;
34 segTree[root*2+2].val += segTree[root].addMark;
35 //传递后,当前节点标记域清空,防止多次向下传递,造成数据错误
36 segTree[root].addMark = 0;
37 }
38 }
39
40 /*
41 功能:线段树的区间查询
42 root:当前线段树的根节点下标
43 [nstart, nend]: 当前节点所表示的区间
44 [qstart, qend]: 此次查询的区间
45 */
46 int query(int root, int nstart, int nend, int qstart, int qend)
47 {
48 //查询区间和当前节点区间没有交集
49 if(qstart > nend || qend < nstart)
50 return -1;
51 //当前节点区间包含在查询区间内
52 if(qstart <= nstart && qend >= nend)
53 return segTree[root].val;
54 //分别从左右子树查询,返回两者查询结果的较小值
55 pushDown(root); //----延迟标志域向下传递(在向下递归之前,首先将延迟标志域向下传递)
56 int mid = (nstart + nend) / 2;
57 return max(query(root*2+1, nstart, mid, qstart, qend),
58 query(root*2+2, mid + 1, nend, qstart, qend));
59
60 }
61
62 /*
63 功能:更新线段树中某个区间内叶子节点的值
64 root:当前线段树的根节点下标
65 [nstart, nend]: 当前节点所表示的区间
66 [ustart, uend]: 待更新的区间
67 addVal: 更新的值(原来的值加上addVal)
68 */
69 void update(int root, int nstart, int nend, int ustart, int uend, int addVal)
70 {
71 //更新区间和当前节点区间没有交集
72 if(ustart > nend || uend < nstart)
73 return ;
74 //当前节点区间包含在更新区间内
75 if(ustart <= nstart && uend >= nend)
76 {
77 segTree[root].addMark += addVal;
78 segTree[root].val += addVal; //最小值当然也加1
79 return ;
80 }
81 pushDown(root); //延迟标记向下传递(在向下递归之前,首先将延迟标志域向下传递)
82 //更新左右孩子节点
83 int mid = (nstart + nend) / 2;
84 update(root*2+1, nstart, mid, ustart, uend, addVal);
85 update(root*2+2, mid+1, nend, ustart, uend, addVal);
86 //根据左右子树的值回溯更新当前节点的值
87 segTree[root].val = max(segTree[root*2+1].val, segTree[root*2+2].val);
88 }
89 int a[MAXN],last[MAXN],pre[MAXN];
90 int dp[MAXN];
91 void build(int root,int nstart,int nend)
92 {
93 segTree[root].addMark=0;
94 if(nstart==nend){
95 segTree[root].val=dp[nstart-1];
96 return ;
97 }
98 int mid=(nstart+nend)/2;
99 build(root*2+1,nstart,mid);
100 build(root*2+2,mid+1,nend);
101 }
102 int main(){
103 //freopen("data.in","r",stdin);
104 int n,k;
105 scanf("%d%d",&n,&k);
106 for(int i=1;i<=n;i++){
107 scanf("%d",&a[i]);
108 pre[i]=last[a[i]];
109 last[a[i]]=i;
110 }
111 for(int j=1;j<=k;j++){
112 build(0,0,n);
113 for(int i=1;i<=n;i++){
114 update(0,0,n,pre[i]+1,i,1);
115 dp[i]=query(0,0,n,0,i);
116 }
117 }
118 printf("%d\n",dp[n]);
119 return 0;
120 }

CodeForces–833B--The Bakery(线段树&&DP)的更多相关文章

  1. Codeforces.833B.The Bakery(线段树 DP)

    题目链接 \(Description\) 有n个数,将其分为k段,每段的值为这一段的总共数字种类,问最大总值是多少 \(Solution\) DP,用\(f[i][j]\)表示当前在i 分成了j份(第 ...

  2. CodeForces 834D The Bakery(线段树优化DP)

    Some time ago Slastyona the Sweetmaid decided to open her own bakery! She bought required ingredient ...

  3. codeforces#426(div1) B - The Bakery (线段树 + dp)

    B. The Bakery   Some time ago Slastyona the Sweetmaid decided to open her own bakery! She bought req ...

  4. Codeforces.264E.Roadside Trees(线段树 DP LIS)

    题目链接 \(Description\) \(Solution\) 还是看代码好理解吧. 为了方便,我们将x坐标左右反转,再将所有高度取反,这样依然是维护从左到右的LIS,但是每次是在右边删除元素. ...

  5. CF833B The Bakery (线段树+DP)

    题目大意:给你一个序列(n<=35000),最多分不大于m块(m<=50),求每个块内不同元素的数量之和的最大值 考试的时候第一眼建图,没建出来,第二眼贪心 ,被自己hack掉了,又随手写 ...

  6. [Codeforces 1197E]Culture Code(线段树优化建图+DAG上最短路)

    [Codeforces 1197E]Culture Code(线段树优化建图+DAG上最短路) 题面 有n个空心物品,每个物品有外部体积\(out_i\)和内部体积\(in_i\),如果\(in_i& ...

  7. Tsinsen A1219. 采矿(陈许旻) (树链剖分,线段树 + DP)

    [题目链接] http://www.tsinsen.com/A1219 [题意] 给定一棵树,a[u][i]代表u结点分配i人的收益,可以随时改变a[u],查询(u,v)代表在u子树的所有节点,在u- ...

  8. HDU 3016 Man Down (线段树+dp)

    HDU 3016 Man Down (线段树+dp) Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Ja ...

  9. Buses and People CodeForces 160E 三维偏序+线段树

    Buses and People CodeForces 160E 三维偏序+线段树 题意 给定 N 个三元组 (a,b,c),现有 M 个询问,每个询问给定一个三元组 (a',b',c'),求满足 a ...

  10. CodeForces 877E DFS序+线段树

    CodeForces 877E DFS序+线段树 题意 就是树上有n个点,然后每个点都有一盏灯,给出初始的状态,1表示亮,0表示不亮,然后有两种操作,第一种是get x,表示你需要输出x的子树和x本身 ...

随机推荐

  1. 深入理解计算机系统 第十章 系统级I/O 第二遍

    了解 Unix I/O 的好处 了解 Unix I/O 将帮助我们理解其他的系统概念 I/O 是系统操作不可或缺的一部分,因此,我们经常遇到 I/O 和其他系统概念之间的循环依赖.例如,I/O 在进程 ...

  2. C# List.sort排序(多权重,升序降序)

    很多人可能喜欢Linq的orderBy排序,可惜U3D里面linq在Ios上会报错,所以就必须使用list的排序. 其实理解了并不难 升序降序比较 sort有三种结果 1,-1,0分别是大,小,相等. ...

  3. ZROI-Day2比赛解题报告

    ZROIDay2-比赛解题报告 版权原因不提供题面信息 序 这几天作息有点鬼畜,虽然昨晚很晚睡但是早上精神还不错,看到题发现T1很友好?T2woc这暴力都好难打?T3多项式?!这样下去比赛会不会出现更 ...

  4. python爬虫redis-ip代理池搭建几十万的ip数据--可以使用

    from bs4 import BeautifulSoupimport requests,os,sys,time,random,redisfrom lxml import etreeconn = re ...

  5. JS-逻辑运算符的与,或,非

    JS-逻辑运算符的与,或,非 1.非 所谓非,就是取反,非真即假,非假即真. 非运算符不仅仅只能用于布尔值,其他数据类型也是可以的,如下: 1.如果操作数是一个对象,返回false 2.如果操作数是一 ...

  6. 阿里巴巴开源框架java诊断工具--Arthas

    下载:arthas wget https://alibaba.github.io/arthas/arthas-boot.jar java -jar arthas-boot.jar --target-i ...

  7. eclipse 导入外面的jar

    Eclipse中导入外部jar包 听语音 | 浏览:52620 | 更新:2014-12-07 20:59 | 标签:eclipse 1 2 3 4 5 6 7 分步阅读 在编写java代码时,为方便 ...

  8. 分布式缓存系统 Memcached 快速入门

    Memcached介绍   官网地址      Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提 ...

  9. 输出指令(echo指令和printf 命令)

    Shell echo命令 Shell 的 echo 指令与 PHP 的 echo 指令类似,都是用于字符串的输出.命令格式: echo string 您可以使用echo实现更复杂的输出格式控制. 1. ...

  10. mysql sleep 死锁例子

    表结构 CREATE TABLE `orders` ( `order_id` int(11) NOT NULL, `order_addr` varchar(255) DEFAULT NULL ) EN ...