Introduction:
This blog covers how a DSU can save all of its previous versions after several union operations which there seems to be a lack of resources to discuss. Thanks to MinaRagy06 for helping me write this blog!
A basic DSU can be implemented in the following way:
Next, I will explain how to store more information to answer queries that require time travelling.
Main Idea:
Problem 1 (Easy) :
Let's start by solving a simple CSES Problem.
The problem can be reduced to binary searching on the first time nodes $$$a$$$ and $$$b$$$ are connected. Now let's learn how to check connectivity during any time using persistent DSU.
We maintain an extra array $$$\text{time_changed}$$$ where it stores the first time a node does not become a root after adding the edges in order. Now we can run $$$\text{get_root}$$$ with an extra parameter $$$\text{time}$$$ to find the root of that node during a certain time and check if nodes $$$a$$$ and $$$b$$$ have the same root at that specific time during binary search.
Time Complexity: $$$O(N + M \cdot {log} N + Q \cdot {log} M \cdot {log} N)$$$
Problem 2 (Medium) :
Consider the following problem, there is a Graph consisting of $$$N$$$ nodes and initially there are no edges you are given $$$Q$$$ queries which you have to solve online of the type:
Add an Edge between node $$$A$$$ and node $$$B$$$
Check Whether Node $$$A$$$ and Node $$$B$$$ are in the same connected component after the $$$X$$$-th Query
Find the Number of Nodes in the connected component of Node $$$A$$$ after the $$$X$$$-th Query
Constraints:
$$$1 \le N,Q \le 2 \cdot 10^5$$$
$$$1 \le A,B \le N$$$
$$$1 \le X_i \le i$$$
In this problem, we also have to use the $$$\text{time_changed}$$$ array. In addition, we need to save the versions of each node such that it was a root after adding an edge in its component along with the size (or any new information we need to save in other problems).
We save two vectors for each node $$$\text{version}$$$ and $$$\text{size}$$$. whenever we add an edge, we push back the time to the new root along with the new total size.
Now whenever we want to get the size of component $$$A$$$ at time $$$T$$$, we find the root of $$$A$$$ at time $$$T$$$ and binary search on the largest index $$$pos$$$ such that $$$version[A][pos] \le T$$$ and return $$$size[A][pos]$$$ as the answer.
Time complexity: $$$O(N + Q \cdot {log} N)$$$
Problem 3 (Hard) :
$$$\textbf{Prerequisites: Persistent segment tree}$$$
Consider the following problem, initially you have $$$M = 1$$$ graphs of $$$N$$$ nodes with no edges, and you are given $$$Q$$$ queries which you have to solve online of the type:
Copy the $$$k$$$-th graph and label the new graph $$$M + 1$$$ and set $$$M = M + 1$$$ then add a new edge connecting nodes $$$A$$$ and $$$B$$$ in this graph.
Check whether node $$$A$$$ and node $$$B$$$ are in the same connected component in the $$$k$$$-th graph.
Find the number of nodes in the connected component of node $$$A$$$ in the $$$k$$$-th graph.
Constraints:
$$$ 1 \le N,Q \le 2 \cdot 10^5 $$$
$$$ 1 \le K \le M $$$
$$$ 1 \le A,B \le N $$$
We can’t do the DSU in the same way mentioned in the previous problem since it allows us to update only the last version of the DSU but here we may have to update an older version. To solve this, we can use a persistent segment tree for every array instead, leaf $$$i$$$ would store the value of parent and size for index $$$i$$$ and any non-leaf won’t store anything except the left and right child. When updating $$$parent_i$$$ and $$$size_i$$$ for some index $$$i$$$ for a particular DSU version $$$k$$$, we can just refer to the node $$$i$$$ in the segment tree with root $$$k$$$ and change the leaf values we need to and label the new root $$$M + 1$$$ which will correspond to the DSU version $$$M + 1$$$.
Time complexity: $$$O( N + Q \cdot {log}^2 N)$$$
Extra Problems:
https://codeforces.me/gym/104468/problem/B
https://oj.uz/problem/view/APIO20_swap
Thanks for reading!