title: Dynamic Programming: USACO Optimal Milking
body: <p>I have been looking at some USACO gold level algorithm problems, and I need someone to help explain the solution of this problem to me.  <a href="http://www.usaco.org/index.php?page=viewproblem2&amp;cpid=365" rel="nofollow noreferrer">Here</a> is the problem, and the solution is below:</p>
  <blockquote>
  <p>Usually the first step in solving incremental-update problems is to solve the problem without the incremental updates, but in this case it can lead one up a blind alley. There is a quite simple DP solution to the non-incremental version of this problem, but it is not at all obvious how to incrementally update this in less than linear time.</p>
  <p>Instead, we can find a divide-and-conquer DP solution that will be easier to update. Divide the range in half, and in each half solve the problem again. In fact, we solve four variants of the problem, depending on whether the left and/or right endpoint is allowed to be used. Given these results from the two halves, we can easily combine them to form the results for the whole range. The ranges form a segment tree over the barn, where each tree node depends on its two children, and the answer is held at the root of the tree.</p>
  <p>Given this tree structure, it is now clear how we can make incremental updates in O(log N) time: an update affects a leaf of the tree, which in turn requires its O(log N) ancestors to be updated.</p>
  </blockquote>
  <p>There is no code in this solution, and I don't really understand how to implement this idea.  I would really appreciate it if someone could explain it more thoroughly or show me how to code it.</p>
  <p>Edit:</p>
  <p>I understand what the solution is saying better now, and I have coded a solution.  However, the code only works for the first two test data.  I thought I programmed what they had said, but I must have made an error.  Here is my code (in Java):</p>
  <pre><code>import java.io.File;
  import java.io.FileWriter;
  import java.io.PrintWriter;
  import java.util.Scanner;
  
  /**
   * Created by jameslennon on 1/19/14.
   */
  public class optmilk {
      static int n, d;
      static int[] m, indx;
      static node[] tree;
  
      public static void main(String[] args) throws Exception {
          Scanner in = new Scanner(new File(&quot;optmilk.in&quot;));
          PrintWriter out = new PrintWriter(new FileWriter(&quot;optmilk.out&quot;));
          n = in.nextInt();
          m = new int[n];
          indx = new int[n];
          d = in.nextInt();
          tree = new node[1 &lt;&lt; n + 1];
          for (int i = 0; i &lt; n; i++) {
              m[i] = in.nextInt();
          }
          construct(1, 0, n - 1);
          int r = 0;
          for (int i = 0; i &lt; d; i++) {
              int a = in.nextInt() - 1, b = in.nextInt();
              update(a, b);
              m[a] = b;
              r += max(tree[1].none, tree[1].both, tree[1].left, tree[1].right);
          }
          //System.out.println(r);
          out.println(r);
          out.close();
          System.exit(0);
      }
  
      private static void update(int a, int b) {
          int i = indx[a];
          int w = 1;
          tree[i].both = b;
          i /= 2;
          while (true) {
              update_node(i, w);
              w *= 2;
              if (i == 1) break;
              i /= 2;
          }
      }
  
      private static int max(int... a) {
          int max = Integer.MIN_VALUE;
          for (int b : a) {
              max = Math.max(max, b);
          }
          return max;
      }
  
      private static void update_node(int i, int w) {
          if (w == 1) {
              tree[i].left = tree[2 * i].both;
              tree[i].right = tree[2 * i + 1].both;
          } else {
              tree[i].none = max(tree[2 * i].none + tree[2 * i + 1].none, tree[2 * i].right + tree[2 * i + 1].none, tree[2 * i].none + tree[2 * i + 1].left);
              tree[i].right = max(tree[2 * i].none + tree[2 * i + 1].right, tree[2 * i].right + tree[2 * i + 1].right, tree[2 * i].none + tree[2 * i + 1].both);
              tree[i].left = max(tree[2 * i].left + tree[2 * i + 1].none, tree[2 * i].both + tree[2 * i + 1].none, tree[2 * i].left + tree[2 * i + 1].left);
              tree[i].both = max(tree[2 * i].left + tree[2 * i + 1].right, tree[2 * i].both + tree[2 * i + 1].right, tree[2 * i].left + tree[2 * i + 1].both);
          }
      }
  
      private static void construct(int i, int a, int b) {
          if (b - a == 0) {
              indx[a] = i;
              tree[i] = new node(0, 0, 0, m[a]);
              return;
          }
          construct(2 * i, a, (a + b) / 2);
          construct(2 * i + 1, (a + b) / 2 + 1, b);
          int both = max(tree[2 * i].left + tree[2 * i + 1].right, tree[2 * i].both + tree[2 * i + 1].right, tree[2 * i].left + tree[2 * i + 1].both);
          if (b - a == 1) both = 0;
          tree[i] = new node(max(tree[2 * i].none + tree[2 * i + 1].none, tree[2 * i].right + tree[2 * i + 1].none, tree[2 * i].none + tree[2 * i + 1].left),
                  max(tree[2 * i].none + tree[2 * i + 1].right, tree[2 * i].right + tree[2 * i + 1].right, tree[2 * i].none + tree[2 * i + 1].both),
                  max(tree[2 * i].left + tree[2 * i + 1].none, tree[2 * i].both + tree[2 * i + 1].none, tree[2 * i].left + tree[2 * i + 1].left),
                  both);
      }
  
      static class node {
          int none, right, left, both;
  
          public node(int n, int r, int l, int b) {
              none = n;
              right = r;
              left = l;
              both = b;
          }
      }
  }
  </code></pre>