DFS Start/Finish Time
======================
In this section, we want to convert a graph into an array using a technique.
There are various ideas to convert a graph into an array, one of which is the starting time. Using this idea, the given graph can be converted into an array, and various problems can be solved more easily with it.
For each vertex, we can consider the first time the DFS algorithm enters it. In this way, each vertex has a unique number, and the vertices can be sorted based on this time to form an array.
Suppose the array we want to create from the graph's vertices is ``a[i]``, and the time when the DFS algorithm enters vertex ``u`` is ``st[u]``. In this case, we place vertex ``u`` at index ``st[u]``, or in other words, ``a[st[u]] = u``.
It is evident that each subtree of the DFS tree corresponds to an interval in the array.
Finishing time is defined similarly to starting time, with the difference that it indicates the time when the DFS algorithm exits a vertex.
Now, we will examine several problems.
We are given a tree with \(n\) vertices and \(q\) queries. In each query, we need to check whether vertex \(u\) is an ancestor of vertex \(v\) or not. The solution should run in \(O(n+q)\) time complexity.
We use the lemma stating the necessary and sufficient condition for an ancestor-descendant relationship as follows: \(stt[u]<=stt[v] and fnt[v]<=fnt[u]\) or \(stt[u]<=stt[v] and stt[v]<fnt[u]\)
The validity of this lemma can be easily verified. To solve the problem, we first perform a DFS traversal on the tree. Then, for each query, we check the aforementioned condition in \(O(1)\) time.
A tree \(n\) vertices along with \(q\) queries is given. In each query, we must find the \(k\) -th ancestor of vertex \(v\). The time complexity is \(O(n+q.log(n))\)
Solution
~~~~
Consider all nodes with height :math:`h[v]-k`. Using the lemma from the previous problem, we conclude that the answer is the node with the maximum starting time (stt) less than the starting time of node :math:`v` among nodes at height :math:`k` or higher. In other words:
- Node :math:`u` with maximum :math:`stt[u]` such that :math:`h[u] = h[v] - k` and :math:`stt[u] \leq stt[v]`.
For each height, construct a vector containing all nodes at that height, sorted by their starting times. This can be done in :math:`O(n)`.
Now, each query reduces to a binary search on one of these vectors!
`Blood Cousins <https://codeforces.com/problemset/problem/208/E>`_
-----------------------------------------------
An :math:`n`-vertex tree is given with :math:`m` queries of the form :math:`v\ p`. For each query, output the number of nodes :math:`u` such that the :math:`p`-th ancestor of :math:`v` and :math:`u` is the same. The solution runs in :math:`O(n + q \lg(n))`.
First, find the p-th ancestor of vertex v
similar to the previous problem. Name this vertex w
.
Now, the answer is the number of vertices u
where:
That is, in the vector corresponding to vertex v
, we want the count of starting times within a specific interval, which can be solved with a simple binary search.
# p-th ancestor of vertex v
w = find_pth_ancestor(v, p)
# The answer is the number of u's such that...
answer = count_us(h, v, w)
# In the vector corresponding to v's height group:
group = height_group[h[v]]
# Binary search for stt[w] <= stt[u] < fnt[w]
left = bisect_left(group, stt[w])
right = bisect_left(group, fnt[w])
return right - left
Given an \(n\)-vertex tree with \(2k\) leaves. In each operation, we can select two leaves and color all edges on the path between them. Find the minimum number of operations required to color all edges, and provide a method achieving this minimum.
The minimum number of operations required is \(k\). One optimal strategy is to pair the leaves appropriately. For example, perform a DFS traversal and pair leaves based on their discovery order.
1void color_edges(Tree T) {
2 vector<Leaf> leaves = T.get_leaves();
3 // For trees with 2k leaves, at most k operations are needed
4 for (int i = 0; i < leaves.size() / 2; i++) {
5 // Color the path between i-th and (i+k)-th leaf
6 auto path = T.find_path(leaves[i], leaves[i + k]);
7 path.color_edges();
8 }
9}
Explanation: Pairing diametrically opposite leaves (in a DFS order) ensures each edge is colored exactly once. This works because every internal edge lies on exactly one such path when leaves are paired systematically. Time complexity remains \(O(n)\).
Answer = k In the following, we present the method and prove its correctness. If n=2, the problem is trivially solved. Therefore, assume n>2 and there exists at least one non-leaf vertex. Root the tree at a non-leaf vertex and number the leaves according to their starting times. Now perform the following pairing on these leaves:
Clearly, the complexity of this pairing is \(O(n)\) .
Now we must show that all edges are colored. The subtree of each edge contains an interval of leaves. For an edge to be colored, there must exist a leaf pair where one end lies inside this interval and the other outside. Suppose the interval corresponding to the target edge is \([l, r]\) . We consider two cases:
First case: \(l \leq k\) and \(k+1 \leq r\) In this case: - If \(l \neq 1\), the pair \((1, k+1)\) falls inside the interval. - Otherwise, the pair \((k, 2k)\) will cover it.
Second case: If the interval doesn't match the first case, assume without loss of generality that \(l, r \leq k\) . In this case, the pair \((r, r+k)\) will color the edge.
Thus, in both cases the target edge is colored, and our constructed pairs are valid.