Appearance
Graph and Tree Intro
Quick Revisit
- USE WHEN: Problem mentions nodes/edges, cities/roads, or any connected-structure vocabulary
- INVARIANT: A tree on n nodes has exactly n-1 edges and is connected; adjacency list stores edges in O(n+m) space
- TIME: O(n+m) for DFS/BFS traversal | SPACE: O(n+m) adjacency list
- CLASSIC BUG: Forgetting to add both directions for undirected edges (adj[u].push_back(v) AND adj[v].push_back(u))
- PRACTICE: ../12-practice-ladders/01-ladder-1100-to-1400.md
Every graph problem on Codeforces assumes you already know 24 vocabulary words—this file teaches all of them with pictures.
Difficulty: Beginner | Prerequisites: Arrays & Strings
Contents
- Intuition
- C++ STL for Graphs
- Implementation (Contest-Ready)
- Operations Reference
- Problem Patterns and Tricks
- Contest Cheat Sheet
- Gotchas and Debugging
- Self-Test
- Practice Problems
- Further Reading
- What to Solve Next
Intuition
Graph theory was born in 1736 when Leonhard Euler proved it was impossible to walk through Königsberg crossing each of its seven bridges exactly once—turning a city stroll into the first theorem of discrete mathematics. Every graph algorithm you learn descends from that single insight: structure, not geometry, determines what paths exist.
Think of a graph as a subway map. Stations are nodes, rail lines are edges. You don't care about the physical distance between stations; you care about which stations connect. That's topology, and it's the entire point.
The moment graphs "click" is when you realize that completely different real-world problems—social networks, road maps, task scheduling, tournament brackets—all reduce to the same dots-and-lines structure. Once you learn BFS on a graph, you can find shortest paths in all of those domains with zero extra work.
Mnemonic: When you see "cities," "people," "tasks," or "computers" connected by "roads," "friendships," "dependencies," or "cables"—you are looking at a graph.
The Problem Statement You Can't Read
You open CF 1092B. It says:
"Given a tree with
nodes, find the diameter."
You know arrays. You know loops. But what is a tree? What is a node? What is a diameter of a tree? The editorial says "root the tree and DFS." You don't know what rooting means. You don't know what DFS operates on.
Every graph problem assumes a shared vocabulary. Without it, you can't even parse the problem statement, let alone solve it. Array problems say "given an array of
This isn't a skill gap. It's a vocabulary gap.
Graphs Are Just Dots and Lines
A graph is a collection of things (nodes) connected by relationships (edges)—and once you learn 24 vocabulary words, you can read any graph problem on Codeforces.
Think of a city map. Cities are dots. Roads between them are lines. That's it. A graph is a map: nodes are cities, edges are roads. Everything else—trees, paths, cycles, components—is just a pattern you can spot in that map.
The analogy is almost perfect. Where it breaks: real maps have geometry, where distances depend on physical space. Graphs are purely topological—only connections matter, not positions.
The 24-Word Visual Dictionary
Below is every term you need, with a picture, a one-line definition, and where you'll see it on Codeforces. Read this section like a picture book—the diagrams are the point.
Vertex (Node)
A thing. An object. A point. Draw it as a number.
text
1 2 3 4 5CF problems say: "
Edge
A connection between two vertices. Draw it as a line.
text
1 ----- 2This edge connects vertex 1 to vertex 2. Written as the pair
Undirected vs Directed
Undirected: the connection goes both ways. A road you can drive in either direction.
text
1 ----- 2 "1 and 2 are connected"Directed: the connection is one-way. A one-way street. Draw with an arrow.
text
1 ----> 2 "1 points to 2" (but 2 does NOT point to 1)A directed graph is called a digraph. CF problems almost always tell you: "undirected" or "directed." If they say "road between
Weighted vs Unweighted
Unweighted: every edge is equal. Just a connection.
text
1 ----- 2 ----- 3Weighted: each edge has a number (cost, distance, time).
text
5 3
1 --------- 2 --------- 3Edge
CF wording: "the road between
Degree
The number of edges touching a vertex.
text
2 3
\ /
\ /
1 ---------- 4
/ \
/ \
5 6Vertex 1 has degree 5: edges to {2, 3, 4, 5, 6}. Vertex 4 has degree 1: edge to {1} only.
For directed graphs, each vertex has:
- In-degree: number of arrows pointing IN
- Out-degree: number of arrows pointing OUT
text
+----> 2
|
1 --+ in-degree(3) = 2
| out-degree(1) = 3
+----> 3 <---- 4Vertex 3: in-degree = 2 (arrows from 1 and 4), out-degree = 0. Vertex 1: in-degree = 0, out-degree = 3.
Handshaking lemma: Sum of all degrees =
text
Degree counting -- same graph undirected vs directed
UNDIRECTED DIRECTED
1 --- 2 --- 3 1 ---> 2 ---> 3
| |
4 v
4
deg 1 = 1 in 1 = 0 out 1 = 1
deg 2 = 3 in 2 = 1 out 2 = 2
deg 3 = 1 in 3 = 1 out 3 = 0
deg 4 = 1 in 4 = 1 out 4 = 0
---------- ------------------
sum = 6 = 2*3 edges sum in = 3 = edgesPath
A sequence of vertices where each consecutive pair is connected by an edge, and no vertex repeats.
text
1 --- 2 --- 3 --- 4 --- 5Path from 1 to 5:
This is NOT a path (vertex 2 repeats):
text
1 -> 2 -> 3 -> 2 -> 4 <-- not a path!Walk
Like a path but can repeat vertices and edges. Less common in CF, but good to know the distinction.
text
1 -> 2 -> 3 -> 2 -> 4 <-- valid walk, not a valid pathCycle
A path that returns to its starting vertex.
text
1 --- 2
| |
| |
4 --- 3Cycle:
A self-loop is a cycle of length 1: an edge from a vertex to itself. A graph with no cycles is called acyclic.
Cycle detection uses DFS back-edges → DFS and Tree DFS.
Connected
An undirected graph is connected if you can reach every vertex from every other vertex by following edges.
Connected:
text
1 --- 2 --- 3
|
4 --- 5Every vertex can reach every other. For example,
NOT connected:
text
1 --- 2 4 --- 5
|
3Vertex 1 cannot reach vertex 4. No path exists between them.
Connected Component
A maximal connected subgraph. Informally: a connected "island" of vertices where you can't add any more vertices without breaking connectedness.
text
Component 1 Component 2 Component 3
+---------+ +---------+ +---+
| 1 --- 2 | | 4 --- 5 | | 7 |
| | | | | | +---+
| 3 | | 6 |
+---------+ +---------+Three components:
A connected graph has exactly 1 component. CF problems often ask: "Find the number of connected components." Find components with BFS/DFS → BFS | DFS.
Complete Graph
Every pair of vertices is connected. Notation:
text
1
/|\
/ | \
/ | \
2---+---3
\ | /
\ | /
\|/
4Bipartite Graph
You can split vertices into two groups so that every edge goes between groups, never within a group. Equivalently: 2-colorable.
text
Group A Group B
+-----+ +-----+
| 1 |-----| 4 |
| |--+ | |
+-----+ | +-----+
|
+-----+ | +-----+
| 2 |--+--| 5 |
| |-----| |
+-----+ +-----+
+-----+ +-----+
| 3 |-----| 6 |
+-----+ +-----+All edges cross between Group A
Test: A graph is bipartite
DAG (Directed Acyclic Graph)
A directed graph with no cycles. Think: task dependencies.
text
1 ----> 2 ----> 4
| ^
| |
+-----> 3 ------+Tasks: 1 must happen before 2 and 3. Both 2 and 3 must happen before 4. No way to follow arrows and return to where you started.
CF keyword: "topological sort," "dependencies," "ordering." → Topological Sort.
Adjacency
Two vertices are adjacent if an edge connects them. That's it.
text
1 --- 2 --- 31 and 2 are adjacent. 2 and 3 are adjacent. 1 and 3 are NOT adjacent.
The neighbors of vertex 2 are
Tree Vocabulary
Trees appear in a substantial fraction of CF problems rated 800–1400. Here's every word you need.
Tree
A connected, acyclic graph with exactly
Any two of these three properties imply the third:
- Connected
- Acyclic (no cycles)
- Exactly
edges
text
1
/ \
2 3
/ \ \
4 5 6
|
77 vertices, 6 edges. Connected. No cycles. It's a tree.
Root
Pick any vertex as the root. This turns an unrooted tree into a rooted tree—suddenly every edge has a direction (toward root = up, away = down).
Same tree, rooted at vertex 1:
text
1 <-- root (depth 0)
/ \
2 3 <-- depth 1
/ \ \
4 5 6 <-- depth 2
|
7 <-- depth 3Rooted at vertex 3 instead:
text
3 <-- root
/ \
1 6
/ \
2 7
/ \
4 5Same tree, completely different shape. The root is a choice, not a property. CF problems say "the tree is rooted at vertex 1" to fix this choice.
text
Rooting transforms edges into parent-child directions
BEFORE -- unrooted AFTER -- rooted at 1
1 --- 2 1
| | / \
| | 3 2
3 4 |
4
No parent or child parent(2) = 1
No depth defined parent(3) = 1
Edges are symmetric parent(4) = 2
depth: 1=0 2=1 3=1 4=2Parent and Child
In a rooted tree, for every non-root vertex
text
1 <-- root
/ \
2 3
/ \
4 5- Parent of 2 = 1. Children of 2 =
. - Parent of 4 = 2. Children of 4 =
(none). - Parent of 1 = none (root has no parent).
Every non-root vertex has exactly one parent.
Leaf
A vertex with no children. In the rooted tree above: 4, 5, 3.
Equivalently, in an unrooted tree: a vertex with degree 1.
text
1
/ \
2 3 <-- leaf (degree 1)
/ \
4 5 <-- both leaves (degree 1)CF problems: "process leaves first," "remove leaves one by one."
Sibling
Vertices that share the same parent.
text
1
/ \
2 3 <-- 2 and 3 are siblings (parent = 1)
/ \
4 5 <-- 4 and 5 are siblings (parent = 2)Depth
The distance from the root to this vertex (counting edges).
text
1 depth 0
/ \
2 3 depth 1
/ \ \
4 5 6 depth 2
|
7 depth 3Height
The longest path downward from a vertex to any leaf below it.
text
1 height 3
/ \
2 3 height 1, height 2
/ \ \
4 5 6 height 0, height 0, height 1
|
7 height 0Subtree
A vertex
text
1
/ \
2 3
/ \ \
4 5 6
|
7Subtree of vertex 2:
(The subtree diagrams above use the same 7-node tree from entry 15.)
Diameter
The longest path between any two vertices in the tree.
text
1
/ \
2 3
/ \ \
4 5 6
|
7Longest path:
The diameter is 5. Finding the diameter is a classic CF problem (two BFS/DFS from any node, then from the farthest node found). → BFS covers the two-BFS diameter trick.
Ancestor and Descendant
In a rooted tree,
text
1
/ \
2 3
/ \
4 5Ancestors of 4:
"Is
The Full Visual Gallery
Six diagrams showing different graph types. Refer back to these when reading CF problem statements.
The first is a simple undirected graph with 5 nodes and 6 edges:
text
1 --------- 2
/ \ / |
/ \ / |
/ \ / |
5 3 --+ |
\ \ |
\ \ |
+----------- 4Vertices:
A directed graph, with in/out degrees annotated in the table below:
text
+----> 2 ----> 5
| ^
1 -----+ |
| |
+----> 3 ----> 4text
+--------+-----------+------------+
| Vertex | In-degree | Out-degree |
+--------+-----------+------------+
| 1 | 0 | 2 |
| 2 | 2 | 1 |
| 3 | 1 | 2 |
| 4 | 1 | 0 |
| 5 | 1 | 0 |
+--------+-----------+------------+Sources (in-degree 0):
A rooted 7-node tree with all labels annotated—this diagram is the single most useful reference for the Tree Vocabulary section:
text
1 <-------- root, depth=0, height=3
/ \
/ \
2 3 <---- depth=1, height(2)=1, height(3)=2
/ \ \
/ \ \
4 5 6 <- depth=2, leaf(4), leaf(5), height(6)=1
|
7 <- depth=3, leaf
parent(7)=6, parent(6)=3, parent(3)=1
children(2)={4,5}
subtree(3)={3,6,7}
diameter path: 4--2--1--3--6--7 (length 5)A graph with two connected components—no edge crosses between the islands:
text
Component A Component B
1 --- 2 5 --- 6
| / | | |
| / | | |
| / | | |
3 --- 4 7 --- 8The complete graph
text
1
/|\
/ | \
/ | \
2---+---4
\ | /
\ | /
\|/
3Every pair connected:
A bipartite graph—no edge stays on the same side:
text
Left Right
(1) ------- (4)
| \ |
| \ |
| \ |
(2) ------- (5)
|
|
(3) ------- (6)Left:
Formal Properties
With the visual dictionary complete, here are the key mathematical facts:
text
+--------------------------------------------------------------+
| GRAPH FACTS |
| |
| * Undirected graph: sum of degrees = 2 * |edges| |
| * Tree with n vertices has exactly n-1 edges |
| * Tree: unique path between any two vertices |
| * K_n has n(n-1)/2 edges |
| * Bipartite <=> no odd cycle |
| * DAG <=> can be topologically sorted |
| * Connected graph with n vertices needs >= n-1 edges |
| * Connected graph with n-1 edges and no cycle => tree |
+--------------------------------------------------------------+Equivalent definitions of a tree (any two imply all):
- Connected + acyclic
- Connected + exactly
edges - Acyclic + exactly
edges - Unique path between every pair of vertices
When to Reach for Graphs
Trigger phrases—scan for these in a problem statement. Any one of them signals a graph:
"Given
cities and roads..." → undirected graph (vertices = cities, edges = roads)
"Given
people and friendship relations..." → undirected graph (vertices = people, edges = friendships)
"Given
tasks with dependencies..." → DAG (vertex = task, directed edge = "must come before")
"Given a tree with
nodes..." → tree ( edges, connected, acyclic, guaranteed)
"Given a rooted tree..." → rooted tree (root specified, parent/child relationships defined)
"Given a directed graph, find if there's a cycle..." → cycle detection (look for back edges in DFS)
"Assign colors to vertices so no two adjacent share a color..." → graph coloring (2-coloring = bipartite check)
Common CF Patterns to Graph Vocabulary
When you read a CF problem, translate the story into graph vocabulary first. This table maps the 12 most common CF phrasings to the exact graph concept and the typical algorithm you'll reach for.
| # | CF Problem Pattern | Graph Concept | Typical Data Structure / Algorithm |
|---|---|---|---|
| 1 | "cities connected by roads" | undirected graph | adjacency list, BFS/DFS |
| 2 | "follow dependencies / prerequisites" | DAG | topological sort (see Topo Sort) |
| 3 | "social network friends" | undirected graph | connected components via BFS/DFS |
| 4 | "one-way streets" | directed graph | DFS, strongly connected components |
| 5 | "minimum connections to link all cities" | MST | Kruskal (see MST) / Prim |
| 6 | "shortest route between two cities" | weighted graph | Dijkstra (see Dijkstra) / BFS (unweighted) |
| 7 | "tournament bracket / elimination" | tree (binary) | DFS, simulate rounds |
| 8 | "org chart / hierarchy / manager chain" | rooted tree | parent array, LCA queries |
| 9 | "grid of cells, move up/down/left/right" | implicit graph (grid) | BFS on 4-directional neighbors |
| 10 | "assign teams so no friends on same team" | bipartite check | BFS 2-coloring (see Bipartite) |
| 11 | "relay messages along a chain" | path graph | simple traversal / DP |
| 12 | "computer network, packets can loop" | general graph with cycles | DFS cycle detection, visited array |
Pattern Fingerprint: "connected components" → BFS/DFS + visited array. "shortest path, unweighted" → BFS. "shortest path, weighted" → Dijkstra. "dependencies" → topological sort. "assign 2 groups" → bipartite check.
Counter-examples—these look like graphs but aren't (yet):
"Given a grid..." → This IS a graph (cells = vertices, adjacent cells = edges), but the grid structure allows simpler representations. See BFS for grid-as-graph patterns.
"Given a string..." → Usually not a graph problem. But sometimes you build a graph from the string (suffix structures, character transitions).
What the Code Won't Teach You
You can read the definition of a tree but not verify one.
A valid tree on
text
Three properties -- any two give TREE
+-------------+
| Connected |--+
+-------------+ |
+---> TREE
+-------------+ |
| Acyclic |--+
+-------------+ |
|
+-------------+ |
| n-1 edges |--+
+-------------+Rooting a tree is a choice, not a property.
The same unrooted tree looks completely different when rooted at different vertices. Parent, child, depth, height, and subtree all depend on which root you pick. An unrooted tree has none of these concepts until you root it.
text
Unrooted Rooted at 1 Rooted at 3
1 -- 2 -- 3 1 3
| / \ |
4 2 3 2
| / \
4 1 4Undirected adjacency list size is
Each undirected edge adj[u] and adj[v]. When estimating memory or iterating all edges, remember the factor of 2. A tree on
text
Graph 1 --- 2 --- 3 m = 2 edges
adj[1] | 2 | 1 entry
adj[2] | 1 | 3 | 2 entries
adj[3] | 2 | 1 entry
-------- --------
total 4 = 2*mC++ STL for Graphs
Graphs have no dedicated STL container—you build them from the usual pieces.
| Container | Usage for Graphs | Example |
|---|---|---|
vector<vector<int>> | Adjacency list (unweighted) | adj[u].push_back(v) |
vector<vector<pair<int,int>>> | Adjacency list (weighted) | adj[u].push_back({v, w}) |
vector<tuple<int,int,int>> | Edge list (weighted) | edges.push_back({w, u, v}) |
queue<int> | BFS frontier | q.push(start) |
vector<bool> | Visited tracking | visited[v] = true |
vector<int> | Parent/depth arrays | parent[v] = u |
text
Adjacency list vs adjacency matrix for the same graph
Graph: 1 --- 2
| / |
| / |
| / |
3 --- 4
ADJACENCY LIST ADJACENCY MATRIX
+---+ 1 2 3 4
| 1 |--> 2 -> 3 1| 0 1 1 0
+---+ 2| 1 0 1 1
| 2 |--> 1 -> 3 -> 4 3| 1 1 0 1
+---+ 4| 0 1 1 0
| 3 |--> 1 -> 2 -> 4
+---+
| 4 |--> 2 -> 3
+---+Graph Representation Comparison
Choosing the right representation is a 10-second decision that affects the rest of your solution. Use this table. (For a deep dive, see Graph Representation.)
| Adjacency Matrix | Adjacency List | Edge List | |
|---|---|---|---|
| Space | |||
| Add edge | |||
| Check if edge (u,v) exists | |||
| Iterate neighbors of v | |||
| Remove edge | |||
| Memory for tree (E=V-1) | |||
| Best use case | Dense graphs, | Sparse graphs, most CP | Kruskal's MST, sorting by weight |
| CF frequency | Rare | ~95% of problems | Edge-sorting problems |
Rule of thumb: If
text
Decision tree for choosing representation
Is V <= 5000 AND you need O(1) edge lookup?
| |
YES NO
| |
v v
+----------------+ Is the algorithm edge-centric
| Adj. Matrix | (Kruskal, Bellman-Ford)?
+----------------+ | |
YES NO
| |
v v
+-----------+ +----------------+
| Edge List | | Adj. List |
+-----------+ | (DEFAULT) |
+----------------+Implementation (Contest-Ready)
Minimal: Build an adjacency list and find degrees
cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<vector<int>> adj(n + 1);
// Invariant: after iteration i, adj stores the first i undirected edges
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
adj[u].push_back(v);
adj[v].push_back(u); // undirected
}
// degree of each vertex
// Invariant: v visits vertices 1..n in order; each printed exactly once
for (int v = 1; v <= n; v++)
cout << "deg(" << v << ") = " << adj[v].size() << "\n";
}Minimal: Read a rooted tree (parent array input)
Many CF problems give a tree as
cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
vector<vector<int>> children(n + 1);
vector<int> parent(n + 1, 0);
// Invariant: after iteration i, children[] and parent[] are set for vertex i
for (int i = 2; i <= n; i++) {
cin >> parent[i];
children[parent[i]].push_back(i);
}
// find leaves (no children)
for (int v = 1; v <= n; v++)
if (children[v].empty())
cout << v << " is a leaf\n";
}Explained: Compute depth and height of a rooted tree
cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
vector<vector<int>> children(n + 1);
for (int i = 2; i <= n; i++) {
int p;
cin >> p;
children[p].push_back(i);
}
vector<int> depth(n + 1, 0);
vector<int> height(n + 1, 0);
// BFS from root to compute depth
// depth[root] = 0, depth[child] = depth[parent] + 1
queue<int> q;
q.push(1);
vector<int> order; // topological order (BFS order = top-down)
// Invariant: every dequeued vertex has its correct depth; order[] is BFS-sorted
while (!q.empty()) {
int v = q.front(); q.pop();
order.push_back(v);
for (int c : children[v]) {
depth[c] = depth[v] + 1;
q.push(c);
}
}
// height computed bottom-up: process nodes in reverse BFS order
// height[leaf] = 0, height[v] = max(height[child]) + 1
reverse(order.begin(), order.end());
// Invariant: when processing v, all descendants already have correct height
for (int v : order) {
height[v] = 0;
for (int c : children[v])
height[v] = max(height[v], height[c] + 1);
}
for (int v = 1; v <= n; v++)
cout << "vertex " << v
<< ": depth=" << depth[v]
<< ", height=" << height[v] << "\n";
}Adjacency List Construction Worked Example
Here is a step-by-step trace of building an adjacency list from scratch—the exact sequence you'd perform in a contest.
Problem statement: "Given
Input format:
text
5 6
1 2
1 3
2 3
2 4
3 5
4 5The graph looks like this:
text
1
/ \
/ \
2 --- 3
| |
| |
4 --- 5Step-by-step construction (1-indexed):
After reading edge 1 2: adj[1]={2}, adj[2]={1} After reading edge 1 3: adj[1]={2,3}, adj[3]={1} After reading edge 2 3: adj[1]={2,3}, adj[2]={1,3}, adj[3]={1,2} After reading edge 2 4: adj[2]={1,3,4}, adj[4]={2} After reading edge 3 5: adj[3]={1,2,5}, adj[5]={3} After reading edge 4 5: adj[4]={2,5}, adj[5]={3,4}
Final adjacency list state:
text
adj[1] --> | 2 | 3 |
adj[2] --> | 1 | 3 | 4 |
adj[3] --> | 1 | 2 | 5 |
adj[4] --> | 2 | 5 |
adj[5] --> | 3 | 4 |
-----------------------
Total entries: 12 = 2 * 6 edges1-indexed version (most CF problems):
cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
// n+1 slots so vertex indices 1..n are valid
vector<vector<int>> adj(n + 1);
// Invariant: after iteration i, adj contains the first i edges (both dirs)
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
adj[u].push_back(v);
adj[v].push_back(u); // undirected: add both directions
}
// Print neighbors
// Invariant: loop visits every vertex exactly once
for (int v = 1; v <= n; v++) {
cout << v << ":";
for (int nb : adj[v]) cout << " " << nb;
cout << "\n";
}
}Output:
text
1: 2 3
2: 1 3 4
3: 1 2 5
4: 2 5
5: 3 40-indexed version (some problems, grid graphs):
cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
// n slots for vertices 0..n-1
vector<vector<int>> adj(n);
// Invariant: after iteration i, adj contains the first i edges (both dirs)
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
u--; v--; // convert 1-indexed input to 0-indexed
adj[u].push_back(v);
adj[v].push_back(u);
}
// Invariant: loop visits every vertex 0..n-1 exactly once
for (int v = 0; v < n; v++) {
cout << v << ":";
for (int nb : adj[v]) cout << " " << nb;
cout << "\n";
}
}Key difference: 0-indexed uses adj(n) and u--; v--;. 1-indexed uses adj(n + 1) and reads vertices as-is. Pick one convention and stick with it for the entire contest. Mixing them is the fastest route to a wrong answer.
Operations Reference
Costs for the basic graph and tree operations you'll use most often:
| Operation | Time | Space |
|---|---|---|
| Build adjacency list from | ||
| Check if | -- | |
| Find degree of vertex | -- | |
| Find all neighbors of | -- | |
| Count connected components (BFS/DFS) | ||
| Compute depth of all nodes (rooted tree) | ||
| Compute height of all nodes | ||
| Find tree diameter (two BFS) |
Problem Patterns and Tricks
"Count Connected Components"
Read the graph, BFS/DFS from each unvisited vertex, count how many times you start a new traversal. That's the component count.
CF examples: CF 977E, CF 1294C.
"Is This Graph a Tree?"
Given
cpp
bool is_tree = (m == n - 1) && (num_components == 1);CF examples: CF 115A, CF 580C.
"Find Leaves / Process from Leaves"
Leaves have degree 1. Many tree problems process leaves first, then remove them (like peeling an onion). This is essentially topological-sort thinking on trees.
CF examples: CF 1238C.
"Bipartite Check / 2-Coloring"
BFS with alternating colors. If you ever try to color a neighbor the same color as the current vertex, the graph is not bipartite.
CF examples: CF 862B, CF 1296E.
"Shortest Path in Unweighted Graph"
BFS from the source. The BFS level equals the shortest distance. This is the single most common use of BFS on Codeforces.
CF examples: CF 1037D, CF 1272D.
"Degree Counting"
Some problems just need you to count degrees: "How many vertices have odd degree?" or "find the vertex with maximum degree."
Handshaking lemma: the number of odd-degree vertices is always even.
CF examples: CF 1133E.
Rating Progression
What graph/tree skills are expected at each Codeforces rating level:
| Rating | What's Expected | Typical Problem |
|---|---|---|
| CF 1200 | Build adjacency list, count components, identify trees, BFS for shortest path in unweighted graph | CF 977E -- Cyclic Components |
| CF 1500 | DFS on trees, compute depth/height, bipartite check, topological sort on DAG | CF 580C -- Kefa and Park (tree DFS with depth) |
| CF 1800 | Tree diameter, LCA basics, DP on trees, multi-source BFS, cycle detection in directed graphs | CF 1092F -- Tree with Maximum Cost |
| CF 2100 | Euler paths, advanced tree decompositions, centroid decomposition, virtual trees, heavy-light decomposition | CF 1153D -- Serval and Rooted Tree |
"What Would You Do If...?"
Scenario 1: You read a problem about
Translation: Directed graph. Check if all vertices can reach vertex 1: reverse all edges, then BFS/DFS from vertex 1. If all vertices are visited, answer is YES.
Scenario 2: A problem gives
Translation: This is a tree (n vertices, n-1 edges, connected). "Farthest cities" = diameter. Use two BFS: first from any node to find the farthest end, then from that end to find the true diameter.
Scenario 3: You're given
Translation: Bipartite check. Build graph with "refuses" as edges. BFS 2-coloring. If any odd cycle exists, answer is NO. See Bipartite Graphs.
Contest Cheat Sheet
text
+--------------------------------------------------------------+
| GRAPH & TREE VOCABULARY CHEAT SHEET |
+--------------------------------------------------------------+
| Graph = vertices + edges |
| Tree = connected + acyclic + (n-1 edges) |
| Degree = # edges at vertex (in-deg + out-deg for directed) |
| Path = no repeated vertices. Cycle = path back to start. |
| Connected = one component. Bipartite = 2-colorable. |
| DAG = directed + no cycles. |
+--------------------------------------------------------------+
| ROOTED TREE QUICK FACTS: |
| depth(root)=0, depth(v)=depth(parent)+1 |
| height(leaf)=0, height(v)=max(height(child))+1 |
| diameter = longest path (find via 2x BFS/DFS) |
| subtree(v) = v + all descendants |
+--------------------------------------------------------------+
| STORAGE: |
| vector<vector<int>> adj(n+1); // adjacency list |
| adj[u].push_back(v); // add edge u->v |
| adj[v].push_back(u); // (both dirs if undirected)|
+--------------------------------------------------------------+
| TREE INPUT (parent array): |
| for(int i=2;i<=n;i++) cin>>p[i], ch[p[i]].push_back(i); |
+--------------------------------------------------------------+"If I Had 5 Minutes"
Five-bullet speed reference—if you forget everything else, remember these:
- Graph = vertices + edges. Store with
vector<vector<int>> adj(n+1). - Undirected edge = TWO push_backs.
adj[u].push_back(v); adj[v].push_back(u); - Tree = connected graph with n-1 edges. No cycles, unique paths.
- BFS = shortest path in unweighted graph.
queue<int>+visited[]. - Degree =
adj[v].size(). Sum of all degrees =(handshaking lemma).
Before You Code
Before writing a single line of graph code, answer these five questions:
- [ ] Is it directed or undirected? (Determines whether you add one or two push_backs per edge)
- [ ] Is it weighted or unweighted? (Determines
vector<int>vsvector<pair<int,int>>for neighbors) - [ ] Is it a tree or general graph? (Trees have n-1 edges and no cycles—skip cycle handling if guaranteed)
- [ ] What's the indexing? (1-indexed →
adj(n+1). 0-indexed →adj(n). Check problem constraints) - [ ] What's the constraint size? (
might allow matrix; requires adjacency list)
Gotchas and Debugging
These are the bugs that cost rounds. Every one below has burned thousands of contestants.
1-indexed vs 0-indexed. CF almost always uses 1-indexed vertices. Declare adj(n + 1), not adj(n). Off-by-one here causes segfaults or wrong answers on every single test case.
Forgetting both directions. For undirected graphs, you must add both adj[u].push_back(v) AND adj[v].push_back(u). Forgetting one direction is the #1 beginner graph bug. Your BFS won't reach half the graph.
Tree has
Root confusion. An unrooted tree has no root, parent, or depth until you pick a root. If the problem doesn't specify a root, you choose one (usually vertex 1). Don't assume a root exists in unrooted problems.
Diameter is edges, not vertices. The diameter of a path
Multi-edges and self-loops. CF problems usually state "simple graph" (no multi-edges, no self-loops). If not stated, your adjacency list might have duplicates. Check problem constraints.
Graph vs tree. A tree is a special graph. If the problem says "tree," you get all tree guarantees for free: connected, acyclic,
Mental Traps
Trap: "I read the definitions, so I know graphs."
Recognizing the word "bipartite" in a problem statement is not the same as translating it into a BFS 2-coloring. Vocabulary gives you parsing ability; only writing the code builds implementation ability. Write the code for every definition, not just the flashcard.
text
WRONG RIGHT
+---------------+ +---------------+
| know the word |----+ | write the BFS |----+
+---------------+ | +---------------+ |
v v
+---------------+ +---------------+
| stuck | | solved |
+---------------+ +---------------+Trap: "Depth and height are the same thing."
Depth is measured down from the root: depth(root) = 0. Height is measured up from the leaves: height(leaf) = 0. At the root of a perfectly balanced tree the values coincide, but at any internal node of an unbalanced tree they can differ wildly. Swapping them silently corrupts DP on trees.
text
WRONG -- same values RIGHT -- different values
1 d0 h0 1 d0 h2
/ \ / \
2 3 d1 h1 2 3 d1 h0
/
4 d2 h0Trap: Visualizing every graph as a tree.
Trees are acyclic, connected, and have unique paths. General graphs can have cycles, multiple components, and many paths between two nodes. If you mentally picture a tree when the problem says "graph," you'll miss cycle handling, visited-array logic, and multi-component iteration.
text
WRONG -- tree assumed RIGHT -- graph has cycles
1 1 --- 2
/ \ | / |
2 3 | / |
/ \ | / |
4 5 3 --- 4The Mistake That Teaches
The bug: You submit CF 977E (Cyclic Components). Your solution finds connected components using BFS, then checks if every vertex in each component has degree exactly 2. It passes the samples but gets Wrong Answer on test 3.
What you wrote:
cpp
// BFS to find components
// Invariant: all vertices in comp[] belong to the same component
vector<int> comp;
// ... BFS fills comp ...
bool is_cycle = true;
// Invariant: is_cycle remains true only if all checked vertices have deg 2
for (int v : comp)
if (adj[v].size() != 2) is_cycle = false;The hidden bug: You used adj(n) instead of adj(n + 1). Vertices are 1-indexed (1 to n), but your adjacency list only has slots 0 to n-1. Vertex n's edges are written to adj[n]—which is out of bounds. No segfault (vectors can silently corrupt), but edges involving vertex n go to the wrong place.
The fix: Change vector<vector<int>> adj(n) to adj(n + 1).
The lesson: When your solution passes samples but fails on larger tests, always check your indexing first. The one-off between 0-indexed allocation and 1-indexed input is the single most common graph bug in competitive programming. Add this to your mental checklist: "Did I allocate n+1?"
text
The timeline of the bug
+-------------------+ +------------------+ +------------------+
| adj(n) allocated | --> | adj[n] is OOB | --> | silent corruption|
| slots 0..n-1 | | vertex n's edges | | wrong component |
+-------------------+ | go nowhere | | wrong answer |
+------------------+ +------------------+Debug Exercises
Find the bug in each snippet. Answers are below—try first!
Bug 1: Undirected Graph—Missing Something?
cpp
int n, m;
cin >> n >> m;
vector<vector<int>> adj(n + 1);
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
adj[u].push_back(v);
// Where's the other direction?
}Bug 2: Tree Reading—Wrong Loop Bound
cpp
int n;
cin >> n;
vector<vector<int>> adj(n + 1);
for (int i = 0; i < n; i++) { // <-- how many edges does a tree have?
int u, v;
cin >> u >> v;
adj[u].push_back(v);
adj[v].push_back(u);
}Bug 3: BFS—Infinite Loop
cpp
queue<int> q;
q.push(start);
while (!q.empty()) {
int v = q.front(); q.pop();
for (int nb : adj[v]) {
q.push(nb); // <-- what's missing?
}
}Bug 4: Degree Computation—Wrong for Directed Graphs
cpp
// Directed graph: count in-degree of each vertex
vector<vector<int>> adj(n + 1); // adj[u] stores outgoing neighbors
for (int v = 1; v <= n; v++)
cout << "in-degree of " << v << " = " << adj[v].size() << "\n";
// ^^ Is adj[v].size() the in-degree?Answers (click to reveal)
Bug 1: Missing adj[v].push_back(u). For undirected graphs you must add both directions. BFS/DFS from vertex u would never discover v as a neighbor, and the graph appears disconnected.
Bug 2: A tree with for (int i = 0; i < n - 1; i++). Reading
Bug 3: Missing a visited[] check. Without if (!visited[nb]) before pushing, the BFS revisits vertices endlessly, causing an infinite loop (or MLE as the queue grows without bound).
cpp
vector<bool> visited(n + 1, false);
visited[start] = true;
queue<int> q;
q.push(start);
while (!q.empty()) {
int v = q.front(); q.pop();
for (int nb : adj[v]) {
if (!visited[nb]) {
visited[nb] = true;
q.push(nb);
}
}
}Bug 4: adj[v].size() gives the out-degree, not the in-degree. For in-degree, you must count how many other vertices have v in their adjacency list—or maintain a separate in_deg[] array while reading edges: for each edge (u,v): in_deg[v]++.
When NOT to Use This
Graph vocabulary is powerful, but not everything is a graph problem:
- Pure math/number theory: If the problem asks for GCD, prime factoring, or modular arithmetic with no connections between objects, don't force a graph model. See Math Toolkit.
- Array manipulation: Prefix sums, sliding window, two pointers—these operate on sequential data. Building a graph from an array adds unnecessary complexity unless the problem explicitly involves connections. See Prefix Sums.
- String problems: Pattern matching (KMP, Z-function) doesn't need graph structure unless the problem involves state machines or suffix automata.
- Greedy on sorted data: If sorting + greedy solves it, a graph model is overkill. See Greedy.
- Small constraints with brute force: If
, bitmask enumeration or brute force is simpler than graph algorithms.
Red flag: If you're building a graph but every vertex has degree 0 or 1 and there are no meaningful traversals, you probably don't need a graph.
Self-Test
Before moving on to BFS/DFS, verify—without looking back—that you can:
- [ ] State the three equivalent definitions of a tree and explain why any two imply the third
- [ ] Write adjacency-list construction for an undirected graph with 1-indexed input—including correct allocation size and both-direction insertion
- [ ] Given a rooted tree diagram, label every vertex with its depth and its height and explain why they differ
- [ ] Explain why
adj[v].size()gives degree in undirected graphs but only out-degree in directed graphs - [ ] State the handshaking lemma and use it to verify your adjacency list has the correct total size
- [ ] Distinguish tree vs forest vs general graph—and state the edge count for each
- [ ] Explain what "simple graph" means and what changes if the graph is not simple
Practice Problems
| # | Problem | Source | Difficulty | Key Idea |
|---|---|---|---|---|
| 1 | Bear and Big Brother | CF 791A | 800 | Not a graph—warm-up. Understand problem parsing first. |
| 2 | King of Thieves | CF 1197A | 800 | Simple pattern—builds reading skills. |
| 3 | Mahmoud and Longest Uncommon Subsequence | CF 766A | 800 | String problem that teaches "read carefully." |
| 4 | Cyclic Components | CF 977E | 1100 | Count connected components where every vertex has degree 2 (cycles). |
| 5 | Two Teams Composing | CF 1335C | 1100 | Degree/counting on implicit graph structure. |
| 6 | DFS Order | CF 580C | 1300 | Tree with root, parent, children—compute depth via DFS. |
| 7 | Nearest Opposite Parity | CF 1272D | 1300 | Build a directed graph from array, BFS for shortest path. |
| 8 | Cat Party | CF 1163B2 | 1100 | Counting—trains reading problem statements with "graph-like" structure. |
Start with problems 4 and 6—they directly use the vocabulary from this file.
Further Reading
- How to store a graph in code: Graph Representation—adjacency list, adjacency matrix, edge list, and when to use each.
- Tree operations and traversals: Tree Basics—DFS on trees, subtree queries, tree DP setup.
- BFS (breadth-first search): BFS—shortest paths in unweighted graphs, level-order traversal.
- DFS and Tree DFS: DFS and Tree DFS—cycle detection, connected components, DFS order.
- Topological Sort: Topological Sort—ordering vertices of a DAG so all edges point forward.
- Shortest paths (weighted): Dijkstra—greedy single-source shortest path for non-negative weights.
- Minimum spanning tree: MST — Kruskal—connect all vertices with minimum total edge weight.
- Graph Theory Basics -- cp-algorithms.com
- Tree Tutorial -- Codeforces Blog by Dardev
What to Solve Next
You now have the full graph and tree vocabulary. Next steps:
- Traverse graphs—BFS for shortest unweighted paths, DFS for components and cycle detection: BFS | DFS and Tree DFS
- Apply graph techniques—bipartite checking, topological sort, shortest paths, and spanning trees: Bipartite | Topo Sort | Dijkstra | MST
- Bridge to the next chapter—see how graph foundations connect to DP and advanced topics: BRIDGE — Graph Foundations → DP
- Practice rated problems that exercise this vocabulary: Ladder 1100–1400
- Choose the right data structure for your next graph problem: Data Structure Selection Guide
- Explore tree-specific structures—segment trees, Fenwick trees, and more: Data Structures