D · Grandmaster · 13 min

DP on Trees

Root the tree. The state is a subtree (and maybe a few flags). Transition combines children. reroot if every node must be the root once.

Knapsack-style DP assumed an array order. On a tree the 'prefix' is a subtree. Classic: size[v] = 1 + Σ size[child]. Classic hard: CSES Tree Matching — take a matching edge to one child or not; CSES Tree Distances II — sum of distances from every node (compute down, then reroot up).

USACO Gold 'DP on Trees'. The WA is treating the tree as a DAG without rooting it, or double-counting the parent as a child.

Subtree sizes, then a matching-style sketchjs
function subtreeSizes(n, edges, root = 0) {
  const adj = Array.from({ length: n }, () => []);
  for (const [u, v] of edges) {
    adj[u].push(v);
    adj[v].push(u);
  }
  const size = Array(n).fill(1);
  function dfs(v, p) {
    for (const u of adj[v]) {
      if (u === p) continue;
      dfs(u, v);
      size[v] += size[u];
    }
  }
  dfs(root, -1);
  return size;
}

TRACE

Same tree as the Euler lesson: 0-1, 0-2-3.

leaf 1

size[1]=1.

1 / 4

CHECK

Tree Matching: maximum number of edges in a matching. A useful state is…

CHECK

You forgot if (u === p) continue. What happens?

Checks 0/2

Next lesson