Chapter 62
HomeTrees are naturally recursive — each node's subtree is itself a tree. DP on trees computes values bottom-up: children compute their answers first, then the parent combines them. This is DFS with memoisation built into the tree structure.
Root the tree at an arbitrary node (usually 0). Run a DFS. For each node,
compute dp[node] from dp[child] of all its children.
The DFS post-order ensures children are processed before their parent.
vector> adj; vector dp; void dfs(int u, int parent) { dp[u] = 1; // count this node for (int v : adj[u]) { if (v == parent) continue; dfs(v, u); dp[u] += dp[v]; // add child's subtree size } } // Usage: dfs(0, -1) // dp[u] = size of subtree rooted at u
💡 The parent parameter
Trees don't have cycles, but DFS can still loop back to the parent. Passing
parent and skipping it avoids this. It's the tree equivalent
of a visited array.
int dfs(int u, int parent) {
int best = 0;
for (int v : adj[u]) {
if (v == parent) continue;
best = max(best, dfs(v, u));
}
return 1 + best; // longest downward path
}
fn dfs(u: usize, parent: usize, adj: &[Vec], dp: &mut Vec ) { dp[u] = 1; for &v in &adj[u] { if v == parent { continue; } dfs(v, u, adj, dp); dp[u] += dp[v]; } }
parent to prevent walking back up the tree.