DSU on Tree
Introduction
What is a heuristic algorithm?
Heuristic algorithms are optimizations based on human experience and intuition for certain algorithms.
For example, the most common one is the heuristic merge in Disjoint Set Union (DSU). The code looks like this:
1 2 3 4 5 6 | |
Here, for two sets of different sizes, we merge the smaller set into the larger one, rather than merging the larger set into the smaller one.
Why? The size of a set can be considered as its height (under normal circumstances), and merging the set with smaller height into the one with larger height obviously helps us find the parent.
Making the tree with smaller height a subtree of the tree with larger height, this optimization can be called the heuristic merge algorithm.
Algorithm Overview
DSU on Tree (dsu on tree) is an algorithm for certain offline tree problems that is faster than or equal to most algorithms and easier to understand and implement.
Consider the following problem: Counting Colors on a Tree.
Example Problem
Given a tree with \(n\) nodes rooted at \(1\), node \(u\) has color \(c_u\). For each node \(u\), ask how many distinct colors appear in the subtree rooted at \(u\).
\(n\le 2\times 10^5\).
For such problems, solutions often involve heavy use of data structures (like tree of trees, etc.). If offline processing is allowed, is there a simpler method?
Process
Since offline queries are supported, consider preprocessing and then outputting answers in \(O(1)\).
Direct brute-force preprocessing has a time complexity of \(O(n^2)\), i.e., traversing each subtree once. The complexity of each traversal is clearly of the same order as \(n\), and with \(n\) nodes, the total complexity is \(O(n^2)\).
We can observe that the answer for each node is derived from its subtree and itself. Consider leveraging this property to solve the problem.
We can first precompute the size of each node's subtree and its heavy son (similar to heavy-light decomposition). The heavy son is the child with the largest subtree. This process can obviously be done in \(O(n)\).
Let \(cnt_i\) denote the occurrence count of color \(i\), and \(ans_u\) denote the answer for node \(u\).
To traverse a node \(u\), we follow these steps:
- First traverse the light (non-heavy) sons of \(u\), compute their answers, but do not retain their impact on the \(cnt\) array;
- Traverse its heavy son, retain its impact on the \(cnt\) array;
- Traverse the light sons' subtrees again, adding their contributions to obtain the answer for \(u\).
The above figure is an example.
Thus, for a node, we traverse its heavy subtree once and its light subtrees twice, which is clearly optimal.
By executing this process, we obtain answers for all subtrees of this node.
Why not merge steps 1 and 3? Because the \(cnt\) array cannot be reused; otherwise, the space would be too large. We need to complete the process within \(O(n)\) space.
Clearly, if a node \(u\) is traversed \(x\) times, its heavy son will be traversed \(x\) times, and its light sons (if any) will be traversed \(2x\) times.
Note: except for the heavy son, the \(cnt\) array must be cleared after each traversal.
Proof
We define heavy edges and light edges as in heavy-light decomposition (edges connecting to heavy sons are heavy edges, others are light edges). For the definitions of heavy son and heavy edge, see the figure below. For a tree with \(n\) nodes:
The number of light edges from the root to any node on the tree does not exceed \(\log n\). Suppose there are \(x\) light edges from the root to a node, and the subtree size of that node is \(y\). Clearly, the subtree size of a child connected by a light edge is less than half of its parent's subtree size (otherwise it wouldn't be a light edge). Thus, \(y < n/2^x\), and obviously \(n > 2^x\), so \(x < \log n\).
Moreover, if a node is the heavy son of its parent, its subtree must be the largest among its siblings. Therefore, any node on the path from an arbitrary node to the root, when computing answers, will not traverse this node via heavy edges. So the number of times a node is traversed equals the number of light edges on the path from it to the root plus \(1\) (the \(+1\) is because the node itself is traversed). Hence, the number of traversals for a node \(= \log n + 1\), and the total time complexity is \(O(n(\log n+1)) = O(n\log n)\). Outputting answers costs \(O(m)\).
The bold edges in the figure are heavy edges, and the child nodes connected by heavy edges are heavy sons.
Optimization
As mentioned in the proof, dsu on tree utilizes the concept of light and heavy sons from heavy-light decomposition to accelerate merging. Therefore, we can also directly use the dfs order obtained from heavy-light decomposition, transforming recursion into iteration, further optimizing the constant factor of dsu on tree.
The dfs order itself has the following property: the subtree of a node is always contiguous in the dfs order. Thus, we can traverse the dfs order array in reverse. This ensures that when processing a node, all other nodes in its subtree have already been processed.
The dfs order obtained from heavy-light decomposition has the following excellent property: a heavy chain is always contiguous in the dfs order. Therefore, when traversing nodes in reverse dfs order, for a node at the top of a heavy chain, the next node to traverse is definitely not its parent, so we need to clear its influence. Otherwise, for nodes not at the top of a heavy chain, the previous node traversed is either its heavy son or a node from another branch whose influence has already been cleared, so we can directly inherit its influence. Based on this, we can quickly collect the influence of all light sons using the dfs order and record the answer.
The above process is called the non-recursive/iterative implementation of dsu on tree (also known as the dfs order implementation of dsu on tree). Compared to the original recursive implementation, it reduces the time and space overhead of recursive function calls, achieving significant constant optimization, especially when dealing with trees containing many chain-like structures, offering notable stack space advantages.
Implementation
Reference Implementation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | |
Applications
-
Problems where the intended solution by some problem setters is dsu on tree
For example, CF741D. Given a tree, each node's weight is a letter from 'a' to 'v'. Each query asks to find a path within a subtree such that the characters on the path, when sorted, form a palindrome.
Since it becomes a palindrome after sorting, a character appearing twice is equivalent to not appearing. That is, the path must satisfy at most one character appears an odd number of times.
The normal approach is to dfs from each node, forcibly enumerating all letters at each node to find paths where the XOR result with it has more than one bit set to 1, then take the maximum length. This is \(O(n^2\log n)\), but can be optimized to \(O(n\log^2n)\) using dsu on tree. For specific details, refer to the extended reading below.
-
Problems that can be solved with dsu tricks
Can be used to score partial points on some tree-of-trees problems (without modification operations). Moreover, the complexity of dsu is better than the \(O(n\sqrt{m})\) of Mo's algorithm on trees.
Practice Problems
Problem translation: Tree nodes have colors. A color dominates a subtree if and only if no other color appears more frequently in that subtree. Find the sum of all colors dominating each subtree.
References/Further Reading
CF741D Author's introduction to dsu on tree
本页面最近更新:,更新历史
发现错误?想一起完善? 在 GitHub 上编辑此页!
本页面贡献者:OI-wiki
本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用