CPP | DSU | Count Sets | TC O (n + m * α(n))
IntuitionCount the redundant connections by checking if findDsu(u) == findDsu(v). If this condition is true, it signifies that u and v are connected by some red
# Intuition
Count the redundant connections by checking if `findDsu(u) == findDsu(v)`. If this condition is true, it signifies that `u` and `v` are connected by some redundant connection. Otherwise, perform `unionFind(u, v)`.
Once the no. of redundant connections is calculated, check how many sets (components of the graph) are disconnected.

Iff, (the redundant connection count) `>=` (the number of disconnected component count - 1), the answer can be calculated as their `(d-1)`. 
Otherwise, it should not be possible to connect the entire graph with the said redundant connections.

# Complexity
- Time complexity: O(n + m * α(n)), where:
`n` is the number of nodes
`m` is the number of given connections bw the nodes.
`α` is the inverse Ackermann function, which grows very slowly. 

- Space complexity: O (n + m)

# Code
```cpp []
class Solution {
public:
    vector<int> parent;
    vector<int> rank;

    int findDsu(int x) {
        if (parent[x] == x) {
            return x;
        }
        return parent[x] = findDsu(parent[x]);
    }

    void unionDsu(int x, int y) {
        int parent_x = findDsu(x);
        int parent_y = findDsu(y);

        if (parent_x == parent_y) {
            return;
        }

        int rank_parent_x = rank[parent_x];
        int rank_parent_y = rank[parent_y];

        if (rank_parent_x > rank_parent_y) {
            parent[parent_y] = parent_x;
        } else if (rank_parent_y > rank_parent_x) {
            parent[parent_x] = parent_y;
        } else {
            parent[parent_x] = parent_y;
            rank[parent_y]++;
        }
    }

    int makeConnected(int n, vector<vector<int>>& graph) {
        vector<vector<int>> adj(n);

        parent.resize(n);
        rank.resize(n, 0);

        for (int i = 0; i < n; i++) {
            parent[i] = i;
        }

        for (int i = 0; i < graph.size(); i++) {
            int u = graph[i][0];
            int v = graph[i][1];

            adj[u].push_back(v);
        }

        int d = 0; // disconnected nodes
        int r = 0; // redundant connections

        for (int u = 0; u < n; u++) {
            for (int v : adj[u]) {
                // check if both u and v have same ultimate leader
                if (findDsu(u) == findDsu(v)) {
                    r++;
                } else {
                    // perform union
                    unionDsu(u, v);
                }
            }
        }

        // count those nodes which are ultimate leader of themselves
        for(int i=0; i<n; i++) {
            if(findDsu(i) == i) {d++;}
        }

        if(r >= (d-1)) {return d-1;}
        else {return -1;}
    }
};
```
['Union Find', 'Counting', 'C++']