The Bakery problem solving report (cf833b dp dp line segment tree with optimization)
NOTICE
Algorithm design
Sample code
# include <cstdio>
# include <cstring>
const int N = thirty-eight thousand and five hundred ; // 35000 * 1.1
int n, k, a[N]; //Data provided in the title
int last[N]; //Record the last occurrence position of the number at this position. Last [i] represents the last occurrence position of the number at a [i] position in a array. If it does not occur, it is 0
int index[N]; //Used to help create last array
int d[N]; //An array used to reserve results in dynamic programming. d [i] represents the results of the last cutting point (or the coordinates of the last cake in the last box) in a cutting (stage)
struct node { int l, r; int f; int max; node *lc, *rc; } mem[ two * N]; node *memp; node *root; //The number of the ith position in the line segment tree is the sum of the number of different numbers in the previous stage d [i] and i+1~j, where j is the current position, see the following explanation for details
inline void push_down (node *p) { if (p->f) { p->lc->max += p->f; p->lc->f += p->f; //Miss take: Like a fool, the line segment tree is wrongly written here p->rc->max += p->f; p->rc->f += p->f; p->f = zero ; } } inline void pull_up (node *p) { if (p->lc->max > p->rc->max) p->max = p->lc->max; else p->max = p->rc->max; } node * init ( int a[], int l, int r) { node *p = memp++; p->l = l; p->r = r; p->f = zero ; if (l == r) { p->lc = p->rc = NULL ; p->max = a[l]; } else { int mid = (l + r) >> one ; // printf("%d %d %d\n", l, mid, r); p->lc = init (a, l, mid); p->rc = init (a, mid + one , r); pull_up (p); } return p; } void change (node *p, int l, int r) { if (l <= p->l && p->r <= r) { ++p->max; ++p->f; return ; } push_down (p); int mid = (p->l + p->r) >> one ; if (l <= mid) change (p->lc, l, r); if (mid < r) change (p->rc, l, r); pull_up (p); } int query (node *p, int l, int r) { if (l <= p->l && p->r <= r) { return p->max; } push_down (p); int mid = (p->l + p->r) >> one ; int max = zero , tmp; if (l <= mid) { max = query (p->lc, l, r); } if (mid < r) { tmp = query (p->rc, l, r); if (tmp > max) max = tmp; } pull_up (p); //Miss take: pull_up here is indispensable
return max; } //
//void debug2() {
// for (int i = 1; i <= n; ++i) printf("%d ", last[i]);
// printf("\n\n");
//}
//
//void debug1() {
// for (int i = 0; i <= n; ++i) printf("%d ", d[i]);
// printf("\n");
//}
int main () { // freopen("input.txt", "r", stdin);
scanf ( "%d%d" , &n, &k); memset (index, zero , sizeof index); for ( int i = one ; i <= n; ++ i) { scanf ( "%d" , a + i); last[i] = index[a[i]]; //In the process of reading the input, fill in the last array at the same time. See the definition above for the meaning of the last array index[a[i]] = i; } //
// debug2();
//For dynamic planning, in order to quickly solve different states in one stage of the dynamic planning, we use recursion, so that the solution order can be optimized by using the line segment tree
memset (d, zero , sizeof d); //It is not difficult to find that the initial results are all 0
for ( int i = one ; i <= k; ++ i) { //Stage, that is, the number of cutting or boxes memp = mem; root = init (d, zero , n); //The line segment tree can be created from 0. See the following explanation for details
for ( int j = i; j <= n; ++ j) { //Enumerate the positions of cutting points at the current stage
//Admittedly, we can run the following cycle for each cutting point
/* int max = 0, tmp; For (int k=i - 1; k<=j - 1;++k) {//It is obviously impossible to satisfy the question before i - 1 tmp = last_d[k] + different_num_between(k, j); if (tmp > max) max = tmp; } */
//But this is too slow. We find that the function has been called many times to calculate the number of different numbers from k to j. It seems that a lot of useless calculations are carried out every time
//We build a line segment tree, which is defined as the number of the kth number in the line segment tree from k+1 (plus one for the convenience of initialization) to the number of different numbers in j when advancing to the position of j
//We can find that there is no a [j] between last [j]+1 and j (nonsense)
//That is to say, if our progress is advanced to j, there should be one more j for all the different types of numbers from the position to j in last [j]+1~j, which is reflected in the quantity, namely number+1
//Don't forget position - 1 when mapping to segment tree
//
//We can also find that, according to the above definition, it is still necessary to scan the entire segment tree to get the maximum value, because we need to add the results of the previous phase to the original segment tree
//Then we simply use the d array as the initial initialization array of the line segment tree when initializing the line segment, so that the maximum value can be calculated with the line segment tree (of course, the line segment tree is written incorrectly when I didn't say)
//
//Stunned? You can read the comments defined there and understand them according to the code
change (root, last[j] + one - one , j - one ); d[j] = query (root, i - one , j - one ); //Miss take: Please note that it must be i - 1 instead of i } // debug1(); } //
////Oh, my old man, this annotated code is a complete error
// int max = 0;
// for (int i = 1; i <= n; ++i) if (d[i] > max) max = d[i];
// printf("%d", max);
// for (int i = 1; i <= n; ++i) printf("%d ", d[i]);
// printf("%d", d[n]);
printf ( "%d" , d[n]); return zero ; }