Codeforces 834D The Bakery - 动态规划 - 线段树

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个非空部分,使得每部分不同的数的个数之和最大。

  显然你需要一发动态规划,用f[i][j]表示前i个位置,已经分成j个非空部分的最大总价值。转移很显然:

   其中w(i, j)表示[i, j]这一段中不同的数的个数。如果这么做的话是,会TLE。考虑如何维护

  有注意到一堆后缀后面增加一个数,不同的数会发生改变的后缀的长度一定是连续的一段,并且这些后缀都不包含这个数。

  因此,我们可以记录一个last[x],表示在当前考虑的位置i之前,x上一次出现的位置在哪。然后就可以找到增加的这一段是哪了。

  对于要支持区间求最大值,区间修改,我们想到了线段树。于是就用线段树去维护dp值对当前位置的贡献。转移的时候查一查最值就好了。

Code

  1 /**
  2  * Codeforces
  3  * Problem#834D
  4  * Accepted
  5  * Time: 857ms
  6  * Memory: 71900k
  7  */
  8 #include<bits/stdc++.h>
  9 using namespace std;
 10
 11 typedef class SegTreeNode {
 12     public:
 13         int val;
 14         int lazy;
 15         SegTreeNode *l, *r;
 16
 17         SegTreeNode():val(0), lazy(0), l(NULL), r(NULL) {        }
 18 //        SegTreeNode(int val, SegTreeNode* l, SegTreeNode* r):val(val), lazy(lazy), l(l), r(r) {        }
 19
 20         inline void pushUp() {
 21             val = max(l->val, r->val);
 22         }
 23
 24         inline void pushDown() {
 25             l->val += lazy, l->lazy += lazy;
 26             r->val += lazy, r->lazy += lazy;
 27             lazy = 0;
 28         }
 29 }SegTreeNode;
 30
 31 #define Limit 4000000
 32 SegTreeNode pool[Limit];
 33 int top = 0;
 34
 35 SegTreeNode* newnode() {
 36     if(top >= Limit)    return new SegTreeNode();
 37     pool[top] = SegTreeNode();
 38     return pool + (top++);
 39 }
 40
 41 typedef class SegTree {
 42     public:
 43         SegTreeNode **rts;
 44
 45         SegTree():rts(NULL) {        }
 46         SegTree(int k, int n) {
 47             rts = new SegTreeNode*[(k + 1)];
 48             for(int i = 0; i <= k; i++)
 49                 build(rts[i], 0, n);
 50         }
 51
 52         void build(SegTreeNode*& node, int l, int r) {
 53             node = newnode();
 54             if(l == r)    return;
 55             int mid = (l + r) >> 1;
 56             build(node->l, l, mid);
 57             build(node->r, mid + 1, r);
 58         }
 59
 60         void update(SegTreeNode*& node, int l, int r, int ql, int qr, int val) {
 61             if(l == ql && r == qr) {
 62                 node->val += val;
 63                 node->lazy += val;
 64                 return;
 65             }
 66             if(node->lazy)    node->pushDown();
 67             int mid = (l + r) >> 1;
 68             if(qr <= mid)    update(node->l, l, mid, ql, qr, val);
 69             else if(ql > mid)    update(node->r, mid + 1, r, ql, qr, val);
 70             else {
 71                 update(node->l, l, mid, ql, mid, val);
 72                 update(node->r, mid + 1, r, mid + 1, qr, val);
 73             }
 74             node->pushUp();
 75         }
 76
 77         int query(SegTreeNode*& node, int l, int r, int ql, int qr) {
 78             if(l == ql && r == qr)    return node->val;
 79             if(node->lazy)    node->pushDown();
 80             int mid = (l + r) >> 1;
 81             if(qr <= mid)    return query(node->l, l, mid, ql, qr);
 82             if(ql > mid)    return query(node->r, mid + 1, r, ql, qr);
 83             return max(query(node->l, l, mid, ql, mid), query(node->r, mid + 1, r, mid + 1, qr));
 84         }
 85
 86         SegTreeNode*& operator [] (int pos) {
 87             return rts[pos];
 88         }
 89 }SegTree;
 90
 91 int n, k;
 92 int *arr;
 93 int res = 0;
 94
 95 inline void init() {
 96     scanf("%d%d", &n, &k);
 97     arr = new int[(n + 1)];
 98     for(int i = 1, x; i <= n; i++)
 99         scanf("%d", arr + i);
100
101 }
102
103 SegTree st;
104 int f[35001][51];
105 int last[35001];
106 inline void solve() {
107     st = SegTree(k, n);
108     memset(f, 0, sizeof(f[0]) * (n + 1));
109     memset(last, 0, sizeof(int) * (n + 1));
110     for(int i = 1; i <= n; i++) {
111         for(int j = 1; j <= k && j <= i; j++) {
112 //                printf("Segment [%d, %d] has been updated.", last[arr[i]] + 1, i - 1),
113             st.update(st[j - 1], 0, n, last[arr[i]], i - 1, 1);
114             f[i][j] = st.query(st[j - 1], 0, n, 0, i - 1);
115             st.update(st[j], 0, n, i, i, f[i][j]);
116 //            cout << i << " " << j << " " << f[i][j] << endl;
117         }
118         last[arr[i]] = i;
119     }
120     printf("%d\n", f[n][k]);
121 }
122
123 int main() {
124     init();
125     solve();
126     return 0;
127 }
时间: 2024-10-07 23:28:56

Codeforces 834D The Bakery - 动态规划 - 线段树的相关文章

Codeforces 444C DZY Loves Colors(线段树)

题目大意:Codeforces 444C DZY Loves Colors 题目大意:两种操作,1是修改区间上l到r上面德值为x,2是询问l到r区间总的修改值. 解题思路:线段树模板题. #include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> using namespace std; const int maxn = 5*1e5; typedef long lo

Codeforces 57B Martian Architecture 暴力||线段树

题目链接:点击打开链接 题意:n长的序列(初始全为0) m个操作 k个查询 下面m个操作[l,r] h 代表 a[l] +=h; a[l+1] += h+i; a[l+i] += h+i;  l<=i<=r 然后问k个位置的和 因为k<=100 所以直接暴力也可以 ----------------------- 如果k<=100000 也是可以做的 只需要给区间记录一个标记lazy,表示从左端点开始 l, l+1, l+i ··· l+r 而向下更新时, 左区间则直接更新, 右区间

Codeforces Round #620 Div2F Animal Observation(前缀和+动态规划+线段树维护)

题意: 作者喜欢观察动物,因此他购买了两个照相机,以拍摄森林中野生动物的视频,一台摄像机的颜色是红色,一台摄像机的颜色是蓝色. 从第1天到第N天,作者将拍摄N天的视频.森林可以分为M个区域,编号从1到M.他将通过以下方式使用相机: 在每个奇数天,将红色相机带到森林中并录制两天的视频. 在每个偶数天,将蓝色相机带到森林中并录制两天的视频. 如果他在第N天使用其中一台摄像机开始录制,则该摄像机仅录制一天. 每个摄像机可以连续观察森林的K个连续的区域. 作者已经获得了有关每天在每个区域看到多少动物的信

codeforces 487B B. Strip(rmq+线段树+二分)

题目链接: codeforces 487B 题目大意: 给出一个序列,要把序列划分成段,每一段最少有L个元素,段中的最大元素和最小元素之差不大于s,问划分的段的最少的数量是多少. 题目分析: 首先用rmq维护区间最大值和区间最小值. 然后按顺序扫描数组,线段树维护的数组,每个记录当前点作为最后一个点的前i个点划分的最小的段数,那么每次更新就是二分找到可以转移到我的最远距离,然后再选取与我距离大于l的那部分,取最小值即可. 最终结果就是线段树维护的数组的最后一个位置的元素的值. AC代码: #in

codeforces 482B. Interesting Array【线段树区间更新】

题目:codeforces 482B. Interesting Array 题意:给你一个值n和m中操作,每种操作就是三个数 l ,r,val.就是区间l---r上的与的值为val,最后问你原来的数组是多少?如果不存在输出no 分析:分析发现要满足所有的区间,而一个点上假如有多个区间的话,这个点的值就是所有区间或的值,因为只有这样才能满足所有区间的,把所有位上的1都保存下来了,那么可以发现用线段树来维护,但是那么怎么判断满不满足条件呢?可以也用线段树,更新了之后在整个维护一遍看看满不满足题意,如

Codeforces GYM 100114 D. Selection 线段树维护DP

D. Selection Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100114 Description When selecting files in an application dialog, Vasya noted that he can get the same selection in different ways. A simple mouse click selects a sing

Codeforces 486E LIS of Sequence(线段树+LIS)

题目链接:Codeforces 486E LIS of Sequence 题目大意:给定一个数组,现在要确定每个位置上的数属于哪一种类型. 解题思路:先求出每个位置选的情况下的最长LIS,因为开始的想法,所以求LIS直接用线段树写了,没有改,可以用 log(n)的算法直接求也是可以的.然后在从后向前做一次类似LIS,每次判断A[i]是否小于f[dp[i]+1],这样就可以确定该位 置是否属于LIS序列.然后为第三类的则说明dp[i] = k的只有一个满足. #include <cstdio>

CodeForces 834D The Bakery

The Bakery 题意:将N个数分成K块, 每块的价值为不同数字的个数, 现在求总价值最大. 题解:dp[i][j] 表示 长度为j 且分成 i 块的价值总和. 那么 dp[i][j] = max(dp[i-1][x]+右边的数的贡献) ( 1<=x < j ). 如果每次都从左到右for过去一定会TLE, 所以我们用线段树来优化这个查询的过程, 并且用滚动数组消去第二维空间. 每次新扫到一个数T, 他就会在上一个T的位置+1 --- 现在这个T的位置产生数目加一的贡献. 然后每次扫完一次

CF-833B The Bakery(线段树优化Dp)

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 decid