Euler Tour Technique
Authors: Benjamin Qi, Andrew Wang, Neo Wang
Flattening a tree into an array to easily query and update subtrees.
Introduction
Focus Problem – read through this problem before continuing!
If we can preprocess a rooted tree such that every subtree corresponds to a contiguous range on an array, we can do updates and range queries on it!
Tutorial
Resources | ||||
---|---|---|---|---|
CPH | introduces tree traversal array, how to solve above problem | |||
SecondThread |
Implementation
After running , each range contains all ranges for each in the subtree of . Also, is equal to the subtree size of .
C++
const int MX = 2e5+5;vector<int> adj[MX];int timer = 0, st[MX], en[MX];void dfs(int node, int parent) {st[node] = timer++;for (int i : adj[node]) {if (i != parent) {dfs(i, node);}}en[node] = timer-1;}
Java
public static void dfs(int i, int p) {st[i] = timer++;for (int next : g[i]) {if(next != p) dfs(next, i);}en[i] = timer-1;}
Example Input
5 3 4 2 5 2 1 1 2 1 3 3 4 3 5 2 3 1 5 3 2 3
Our example input corresponds to the following graph:
Before the DFS, our and arrays are initialized with zeros. In this visualization, the first row represents the node indices, the second row represents , and the third represents .
Current timer value: 0
Node Index | 1 | 2 | 3 | 4 | 5 |
st[i] | 0 | 0 | 0 | 0 | 0 |
en[i] | 0 | 0 | 0 | 0 | 0 |
Since we call , we set to the current timer value of . Then, we proceed to call and .
Current timer value: 1
Node Index | 1 | 2 | 3 | 4 | 5 |
st[i] | 0 | 0 | 0 | 0 | 0 |
en[i] | 0 | 0 | 0 | 0 | 0 |
Now we must resolve and . It does not matter which order we process these in, so for this example, we start with . Since the timer value is 1, we set to 1 and increment the timer. However, because node has no children, we don't call . Instead, we set to 1 because our current timer is now 2.
Current timer value: 2
Node Index | 1 | 2 | 3 | 4 | 5 |
st[i] | 0 | 1 | 0 | 0 | 0 |
en[i] | 0 | 1 | 0 | 0 | 0 |
Now we must resolve . Similar to before, we set to the value of the timer (2 in this case) and increment the timer. Then, we proceed to make the calls and .
Current timer value: 3
Node Index | 1 | 2 | 3 | 4 | 5 |
st[i] | 0 | 1 | 2 | 0 | 0 |
en[i] | 0 | 1 | 0 | 0 | 0 |
Now we must resolve and . First, we resolve by setting to the value of the timer (3 in this case) and incrementing the timer. Then, since node has no children, set to 3.
Now the value of the timer is 4, and we must resolve . Similar to before, we set to 4. Since node also has no children, set to 4.
Current timer value: 5
Node Index | 1 | 2 | 3 | 4 | 5 |
st[i] | 0 | 1 | 2 | 3 | 4 |
en[i] | 0 | 1 | 0 | 3 | 4 |
Now, we must resolve the remaining calls. We first encounter and resolve node , setting to 4. We then do the same for node , setting to 4. Our DFS traversal is now complete.
Node Index | 1 | 2 | 3 | 4 | 5 |
st[i] | 0 | 1 | 2 | 3 | 4 |
en[i] | 4 | 1 | 4 | 3 | 4 |
Solution - Subtree Queries
Assumes that dfs()
code is included. Uses a segment tree.
C++
/*** Description: 1D point update, range query where \texttt{comb} is* any associative operation. If $N=2^p$ then \texttt{seg[1]==query(0,N-1)}.* Time: O(\log N)* Source:* http://codeforces.com/blog/entry/18051* KACTL* Verification: SPOJ Fenwick*/
Java
import java.util.*;import java.io.*;public class Main {public static int[] st;public static int[] en;public static int timer, n;public static ArrayList<Integer> g[];//Segtree code
LCA
Focus Problem – read through this problem before continuing!
Focus Problem – read through this problem before continuing!
Tutorial
Resources | ||||
---|---|---|---|---|
CPH | ||||
cp-algo |
Implementation
Resources | ||||
---|---|---|---|---|
Benq |
C++
int n; // The number of nodes in the graphvector<int> graph[100000];int timer = 0, tin[100000], euler_tour[200000];int segtree[800000]; // Segment tree for RMQvoid dfs(int node = 0, int parent = -1) {tin[node] = timer; // The time when we first visit a nodeeuler_tour[timer++] = node;for (int i : graph[node]) {if (i != parent) {
Java
import java.util.*;import java.io.*;public class LCA {public static int[] euler_tour, tin;public static int timer, size, N;public static ArrayList<Integer> g[];//Segtree codepublic static final int maxsize = (int)1e7; // limit for array size
Sparse Tables
The above code does time preprocessing and allows LCA queries in time. If we replace the segment tree that computes minimums with a sparse table, then we do time preprocessing and query in time.
Focus Problem – read through this problem before continuing!
Resources
Resources | ||||
---|---|---|---|---|
CPH | diagrams | |||
PAPS | code | |||
cp-algo |
Optional: Faster Preprocessing
From CPH:
There are also more sophisticated techniques where the preprocessing time is only , but such algorithms are not needed in competitive programming.
Ex. the following:
Implementation
C++
Resources | ||||
---|---|---|---|---|
Benq |
Problems
Status | Source | Problem Name | Difficulty | Tags | |
---|---|---|---|---|---|
CSES | Easy | Show TagsEuler Tour, PURS | |||
CSES | Normal | Show TagsEuler-Tree, PURS | |||
Gold | Normal | Show TagsEuler-Tree, HLD, PURS | |||
Gold | Normal | Show TagsEuler-Tree, LCA | |||
Plat | Normal | Show TagsEuler-Tree, PURS | |||
AC | Normal | Show TagsBinary Search, Euler Tour | |||
IOI | Hard | Show TagsBinary Search, Euler-Tree | |||
Plat | Hard | Show TagsEuler-Tree, PURS | |||
CF | Hard | Show TagsEuler Tour, RURQ | |||
DMOJ | Very Hard | Show TagsEuler-Tree, PURS |