The Bakery problem solving report (cf833b dp dp line segment tree with optimization)

2018-05-22

http://codeforces.com/contest/833/problem/B

This article is migrated from the old blog. The original link is https://lookas2001.com/the-bakery-%e8%a7%a3%e9%a2%98%e6%8a%a5%e5%91%8a/

NOTICE

It is recommended to read before writing this question HH's necklace problem solving report (SDOI 2009 line segment tree sorting offline processing)

Algorithm design

Get the question, 3 minutes, this is a dynamic planning. Write code, debug, submit, and complete in one go. Finally, it was found that the timeout occurred at the fourth point. Looking back, we found that there was a loop in processing each state, which led to the abandonment of the overall algorithm complexity O (n ^ 3).

Recently, I learned the line segment tree, and then looked at this problem. I found that this problem carried out many unnecessary repeated operations when calculating the number of different numbers, which can be solved by using the line segment tree.

Because my description is really not good, please look directly at the following code and its comments for specific solutions. First, we will have a rough look at the definition of global variables, and then we will look at the main function.

It's better to eat with this picture.

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 ; }
Maintenance of the website requires a certain amount of expense. If you agree with this article, please close the advertisement blocker and browse the advertisement. Thank you!
Loading

(∀) In other words, welcome to the small station of lookas!

This is the place where lookas records some things. From time to time, there may be some magical brain holes or unreliable ideas of lookas.

Anyway, let's have a look.