Rare
 0/3

Critical

Authors: Benjamin Qi, Mihnea Brebenel

CSES Critical Cities and Extensions

Edit This Page

Prerequisites

  • Gold - Cycle Finding

Paths

Focus Problem – try your best to solve this problem before continuing!

Resources
Wiki

Wiki Definition

Blog

Well-covered article

Blog

Well-covered article

Solution

Naive approach

Critical nodes in a DAG are commonly known as dominators. Let's define Dom(u)Dom(u) as the set of nodes that dominate node uu.

The dominator of the starting node is itself. The set of dominators for any other node uu is the intersection of the set of dominators for all ancestors pp of node uu.

Dom(u)={u, if u is the starting pointu(pancestor(u)Dom(p))Dom(u)= \begin{cases} u,\text{ if u is the starting point}\\ {u} \cup (\cap_{p \in ancestor(u)} Dom(p)) \end{cases}

The following code uses the above rucerrence.

C++

#include <bits/stdc++.h>
using namespace std;
const int NMAX = 1e5;
int n, m;
vector<int> g[NMAX];
vector<bitset<NMAX>> dom;
bool seen[NMAX];

Time complexity: O(NM)\mathcal{O}(N \cdot M).

Unfortunately, this is too slow.

A faster approach

In this approach we attempt building the dominator tree of the graph. A dominator tree is a tree where each node's children are thos nodes it immediately dominates. The start node is the root of the tree. The immediate dominator - or shortly idom - of a node uu is a node vv that strictly dominates node uu and every other dominator of node uu strictly dominates node vv. The definition of the dominator tree gives us the following property: If node vv dominates node uu, then node vv is an ancestor of node uu, thus the answe of the problem is the path from the root to node nn in the dominator tree.

From now on, we'll discuss the construction of the dominator tree, but first we need to precompute some helpful values:

e(u)=the entry time in node u during a DFS(0)e(u) = \text{the entry time in node u during a DFS} \tag{0}
sdom(u)=min(vthere is a path from v to u: v=v0v1v2v3vk=u such that e(vi)<e(u),i[1,k])sdom(u)= min(v \vert \text{there is a path from v to u: } v = v_0 \rightarrow v_1 \rightarrow v_2 \rightarrow v_3 \dots \rightarrow v_k = u \\ \text{ such that } e(v_i)<e(u), \forall i \in [1,k])

The following graph depicts the semi-dominator for every node. The full-color edges represent the edges part of the DFS tree.

Graph2

Illustrated dominators

Important properties

If you're interested in seeing proofs scroll down to the final section.

  1. If uSu \neq S, then sdom(u)sdom(u) is a proper ancestor of uu is the DFS tree.
  2. If uSu \neq S, then idom(u) is an ancestor - not necessarily proper - of uu in the DFS tree.
  3. Let uSu \neq S. If, for every vertex xx which is an ancestor of uu and has sdom(u) as its proper ancestor, sdom(x)sdom(u)sdom(x) \geq sdom(u) then idom(u)=sdom(u)idom(u)=sdom(u).
  4. Let wSw \neq S. Let u be a vertex for which sdom(u)sdom(u) is minimum among all vertices u satisfying “sdom(w)sdom(w) is a proper ancestor of uu and uu is an ancestor of ww”. If sdom(u)sdom(w)sdom(u) \leq sdom(w) then idom(w)=idom(u)idom(w) = idom(u).
  5. Let uSu \neq S and &v& be a vertex for which sdom(v)sdom(v) is minimum among all vertices uu satisfying: sdom(u)sdom(u) is a proper ancestor of vv and vv is an ancestor of u. If sdom(v)sdom(u)sdom(v) \le sdom(u) then idom(u)=idom(v)idom(u) = idom(v).
  6. Let wSw \neq S. Let uu be a vertex for which sdom(u)sdom(u) is minimum among all vertices uu satisfying “sdom(w)sdom(w) is a proper ancestor of uu and uu is an ancestor of ww”. Then:
idom(w)={sdom(w), If sdom(u)=sdom(w),idom(w),otherwise.(1)idom(w)=\begin{cases} sdom(w), & \text{ If } sdom(u)=sdom(w),\\ idom(w), & \text{otherwise}. \end{cases} \tag{1}

Precompute Sdom

The following identity enables us the compute sdomsdom:

sdom(u)=min((y(y,u)E and yu)(sdom(x)x>u and there is and edge (x, y) such that x is ancestor of y))(2)sdom(u) = min((y \vert (y, u) \in E \text{ and } y \le u) \cup (sdom(x) \vert x>u \\\text{ and there is and edge (x, y) such that x is ancestor of y}) ) \tag{2}

In other words, sdom(u)sdom(u) is the minimum node in the intersection of the following groups:

  1. All the nodes yy with yu and (y,u)Ey \le u \text{ and } (y,u) \in E
  2. sdom(x)sdom(x) where x>u and (u,y)Ex > u \text{ and } (u, y) \in E

Implementation

  1. Precompute using a DFS the new mapping of the tree, i.e. the new labels of the nodes are their entry time u=e[u]u = e[u].

  2. Compute sdomsdom for all nodes by applying the formula mentioned in the previous section. Iterate over node in decreasing order, maintaining the visited nodes in a DSU. Also, the DSU is slightly modified:

  • Find(u)Find(u) = Let xx be the root of the components
    • If u=xu=x, return xx;
    • else return a node vv with minimum sdom, lying on path from uu to xx.

In order to process node uu we iterate over all incoming edges (v,u):(v, u):

  • If v<uv < u, vv is an ancestor of uu, vv would not have been processed till now, thus Find(v)Find(v) would return v.
  • If v>wv > w then vv would have already been processed and Find(v)Find(v) would return a node xx lying on the path from vv to root in the DSU tree having minimum sdom.

The 2 cases cover precisely the definition (2)(2).

  1. Compute the idomidom for all nodes by applying (1)(1).

In this step, store nodes in buckets of their own sdomsdom and iterate over them updating the idomidom value using (1)(1).

This snippet of code combines steps 22 and 33:

C++

// Iterate decreasingly
for (int i = n - 1; i >= 0; i--) {
// Iterat over incoming edges applying (2)
for (int p : rg[i]) { sdom[i] = min(sdom[i], sdom[Find(p, 0)]); }
// Insert i into it's bucket after sdom[i]
if (i > 0) { bucket[sdom[i]].push_back(i); }
// Apply (1) for nodes in bucket
for (int node : bucket[i]) {
int p = Find(node, 0);
if (sdom[p] == sdom[node]) {
  1. After steps 22 and 33, the idomidom value has been set (=sdom=sdom), for every node uu, or it has been set to an ancestor vv of uu and idom(u)=idom(v)idom(u)=idom(v). Now if we start processing nodes after (0) increasingly, then for any vertex uu:
If sdom(u)idom(u) then idom(u)=idom(idom(u))else idom(u)=sdom(u)\text{If } sdom(u) \neq idom(u) \text{ then } idom(u) = idom(idom(u)) \\ \text{else } idom(u) = sdom(u)

C++

#include <functional>
#include <iostream>
#include <set>
#include <stack>
#include <vector>
using namespace std;
int main() {
int n, m;

Time complexity: O((N+M)logN)\mathcal{O}((N+M) \cdot \log{N})

Proofs

Cycles

StatusSourceProblem NameDifficultyTags
CodeforcesHard
KattisHard

Module Progress:

Join the USACO Forum!

Stuck on a problem, or don't understand a module? Join the USACO Forum and get help from other competitive programmers!