Outline
- Problem
- Observations
- Algorithm
- Pre-Processing
- Answering Queries
- Queries based on number of edges added
- Queries based on nodes
- Complexity Analysis
- Example Problems
- Problem L, GP of Zheijang
- CF1989F
- ONTAK 2009 Godzilla
Hello, in this blog I'll explain the algorithm from this Radewoosh blog. The linked blog is understandable and well-written (and honestly, this idea can be in summarized in one line as "dnc over timeline") but it's a bit informal and I feel like writing something, hence this blog.
Problem
We are going to learn to maintain information about SCCs in a directed graph when have a problem with the following general structure:
Given an initially empty digraph
, and a list of directed edges that are sequentially added to the graph, we need to answer queries of the following form: Find some information about SCCs in after the first edges are added
Some specific examples of queries would be:
- Find the smallest
such that and belong to the same SCC after the first edges are added. - Find the size of the SCC containing node
after edges are added.
Note: As the title suggests, the idea described here is only applicable when we are allowed to process the queries offline.
Observations
Notation:
Define
The entire algorithm is based around a trivial observation:
For every pair of nodes
we have, .
Therefore, for every edge

Algorithm
Pre-Processing
We will be doing DnC over the timeline of addition of edges (ie,
Let us look at what we do in the first call,
- Define
. We add all edges to our initially empty graph and find SCCs induced by these edges. This can be done in time where is the number of edges. - Store information about SCCs/answer queries.
- For every edge
, if , then we can deduce that . Otherwise, . Separate these edges into two groups and respectively. Add all edges to . - Call
. - Once we return from this call, compress all the SCCs induced by the addition of edges in
. - Call
. **Remember, this call must operate on the compressed graph.
Notice that when we execute
The pseudo code for our algorithm takes the following form:
create an empty graph G with n nodes
def f(l, r, edge list):
mid = (l + r)/2
initialize SCC structure from G
for every edge E[i] = (u -> v) in edge list:
if(i <= mid)
add (u -> v) to SCC structure
compute SCCs
problem specific stuff (storing info/answering queries)
initialize empty edge lists G1 and G2
for every edge E[i] = (u -> v) in edge list:
if(i <= mid):
if(SCC[u] = SCC[v])
add E[i] to G1
else
add E[i] to G2
else
add E[i] to G2
f(l, mid - 1, G1)
f(mid + 1, r, G2)
for every SCC induced by G1:
compress SCC into a single node in G
enddef
f(1, m, edge list)
Answering Queries
This is quite problem-dependent. I have only seen a couple of problems which use this idea so I will just describe two ways of answering queries that I know of:
Queries based on number of edges added
These queries are of the form:
Find some information about the SCCs of a graph after
edges are added.
Such queries can be answered by maintaining SCCs using DSU/Small-to-Large merging/(some other ds) as we go through the recursion in a way such that after performing the computation for
Note: This is possible because if the SCC compression for
Queries based on nodes
This is a somewhat non-trivial technique, which can be used to answer queries like:
Find the smallest
such that nodes and belong to the same SCC after the first edges are added to
We solve it in the following way:
- We maintain an initially empty undirected weighted graph
. - At every call
, for every edge , we add an edge with weight to . - Finally,
contains edges, with weights . - To answer a query for a pair of nodes
, one can observe that the problem is equivalent to finding the minimized maximum weight edge on any path between and in . This can be done by constructing the MST of and answering path maximum queries.
Note: Since all edges in
Complexity Analysis
Since we split apart
The time taken for answering queries is problem dependent.