Trees
Tree definitions and terminology
What is a tree?
A tree is an undirected graph that is:
- Connected (every node can reach every other node)
- Acyclic (contains no cycle)
Equivalent definitions which some problems use:
- A graph with n nodes is a tree iff it is connected and has n −1 edges.
- A graph with n nodes is a tree iff it is acyclic and has n-1 edges.
- A graph is a tree iff between any two nodes in the graph, there is exactly one simple path.
A forest is a collection of trees.
When a problem explicitly gives you a tree, they usually either give you an edge list for unrooted trees or a parent array for rooted trees.
Rooted vs unrooted trees
The above definition of a tree is equivalent to the definition of an unrooted tree. However, it can be beneficial to root a tree at a specific node. You can think of this as drawing the root of the tree at the top, the root's neighbors (children) below, and so on.
In contest problems, a very standard move is to root an unrooted tree to make the problem easier to reason about.
More Tree Terminology
- Degree: number of incident edges to a node
- Leaf:
- In an unrooted tree: nodes with degree
- In a rooted tree: a node with no children
- Parent of a node: the unique node directly above it on the path from it to the root. The root has no parent.
- Child: a is a child of b if b is a's parent
- Ancestor/Descedant: these definitions extend intuitively from those of parent/child. We say a is an ancestor of b iff a lies on the path from b to the root and a is a descendant of b iff b is an ancestor of a.
- Depth of a node (rooted): distance from the root in number of edges
- Height of a tree (rooted): maximum depth of a node in the tree
- Subtree of v (rooted): v plus all descendants of v
- Diameter: the longest distance between any two nodes
In most tree problems, we choose dfs as our traversal and we start from the root. Some simple examples to get started:
Tree DP (dynamic programming on trees)
The general pattern:
Most simple tree DP's are:
- Pick a root (or use the given root)
- Solve each subtree
- Combine child answers at the parent
- We often store many dp values at a node and have to find an efficient way to recover the answers for our parents
Key reason it works:
- Subtrees do not overlap, so you can do clean “child → parent” transitions.
Problems:
"Rerooting DP":
Binary jumping/Sparse Table
Binary jumping
Let's begin with: Company Queries I (CSES)
Precompute “jump pointers” so you can move up the tree in powers of two:
- 1 step up
- 2 steps up
- 4 steps up
- 8 steps up