Explanation of Intuition and step by step building of code
TLDRReverse the direction of all edges and use Prim's to find Minimum spanning tree.Additionally, it is my first post and any tips to improve the post would be
# TLDR
Reverse the direction of all edges and use Prim's to find Minimum spanning tree.

Additionally, it is my first post and any tips to improve the post would be really appriciated. 
The code might not be the best and most optimzied code since it was written in a time constraint manner during the competition.
# Intuition
<!-- Describe your first thoughts on how to solve this problem. -->
The conditions in the question state that node 0 should be reachable by every other node, but if we use BFS/DFS on every node, we will exceed time complexity. BUT, this also means that if we reverse the direction of every edge, every node should be reachable by node 0.
Now, we serve 2 simple conditions,
1. Take the edges with minimum weight - We can use priority queue for this
2. Keeping the threshold in check - We use a vector to keep track of the incoming edges to a node (since the edges are reversed, it means outgoing edges)

*(Always try to implement a probable solution if you do not know the answer, it works for me more than sitting with pen and paper and implementing nothing)*
# Approach
<!-- Describe your approach to solving the problem. -->
If we look at it broadly, after reversing the edges, we just have to find the Minimum Spanning Tree. (I used Prim's because I find it easier to code)
1. We initialize an adjacency matrix which reverses the direction of current edges
```cpp []
    int minMaxWeight(int n, vector<vector<int>>& edges, int threshold) {
        // Vector of {end_node, weight}
        map<int, vector<vector<int>>> adj;
        for(auto edge: edges) {
            adj[edge[1]].push_back({edge[0], edge[2]});
        }
    }
```

2. We declare all the required data structures needed for Prim's algorithm
```cpp []
    struct custsort{
        bool operator() (vector<int> const &a, vector<int> const &b) {
            return a[2] > b[2];
        }
    };
    int minMaxWeight(int n, vector<vector<int>>& edges, int threshold) {
        // Vector of {end_node, weight}
        // map<int, vector<vector<int>>> adj;
        // for(auto edge: edges) {
        //    adj[edge[1]].push_back({edge[0], edge[2]});
        // }
        priority_queue<vector<int>,vector<vector<int>>, custsort> pq; // Priority Queue to sort the edges by their weights
        vector<int> thres(n, 0); // Keeps track of threshold for every node
        vector<int> visited(n, 0); // Keeps track if a node is visited or not
        vector<int> connected_nodes; // Vector of all connected nodes
    }
```
3. Next, we push node 0 in our connected nodes list and its edges in to the priority queue
```cpp []
    int minMaxWeight(int n, vector<vector<int>>& edges, int threshold) {
        // Vector of {end_node, weight}
        // map<int, vector<vector<int>>> adj;
        // for(auto edge: edges) {
        //    adj[edge[1]].push_back({edge[0], edge[2]});
        // }
        // priority_queue<vector<int>,vector<vector<int>>, custsort> pq; // Priority Queue to sort the edges by their weights
        // vector<int> thres(n, 0); // Keeps track of threshold for every node
        // vector<int> visited(n, 0); // Keeps track if a node is visited or not
        // vector<int> connected_nodes; // Vector of all connected nodes
        connected_nodes.push_back(0);
        visited[0] = 1;
        for(auto edge: adj[0]) {
            pq.push({0, edge[0], edge[1]});
        }
    }
```
4. Finally, we write our core logic, keeping the following things in mind
- If connected_nodes.size() == number of nodes, we exit the loop
- If priority_queue is empty, we were not able to connect all nodes, thus return -1

``` cpp []
    int minMaxWeight(int n, vector<vector<int>>& edges, int threshold) {
        // Vector of {end_node, weight}
        // map<int, vector<vector<int>>> adj;
        // for(auto edge: edges) {
        //    adj[edge[1]].push_back({edge[0], edge[2]});
        // }
        // priority_queue<vector<int>,vector<vector<int>>, custsort> pq; // Priority Queue to sort the edges by their weights
        // vector<int> thres(n, 0); // Keeps track of threshold for every node
        // vector<int> visited(n, 0); // Keeps track if a node is visited or not
        // vector<int> connected_nodes; // Vector of all connected nodes
        // connected_nodes.push_back(0);
        // visited[0] = 1;
        // for(auto edge: adj[0]) {
        //     pq.push({0, edge[0], edge[1]});
        // }
        int ans = 0; // Stores our answer
        while(pq.size()>0) {
            if(connected_nodes.size()==n)
                return ans;
            vector<int> t = pq.top(); // temporary vector
            pq.pop();
            // If we have already visited the node or we are crossing the threshold, skip it
            if(visited[t[1]] || thres[t[1]]==threshold)
                continue;
            visited[t[1]] = 1;
            thres[t[1]]++;
            connected_nodes.push_back(t[1]);
            // Compare the weight of the edge added with the current answer
            ans = max(ans, t[2]);
            for(auto edge: adj[t[1]]) {
                pq.push({t[1], edge[0], edge[1]});
            }
        }
        // At the end, if we could not connect all nodes, return -1
        if(pq.size()==0 && connected_nodes.size()<n)
            return -1;
        return ans;
    }
```

# Complexity
- Time complexity: ```O((V+E)logV)``` (Same as Prim's Algorithm)

```O(ElogE)``` for creating adjacency list.
```O(E)``` since we iterate directly on the vector of edges and ```logE``` to push every edge into the map. (We can also use unordered_map for better results)
```O((V+E)logE)``` For Prim's algorithm
```O(logE)``` since we iterate on every edge in priority queue, ```O(V+E)``` because we want to visit every node and push every edge into the priority queue (All in worst case)

# Code
```cpp []
class Solution {
public:

    struct custsort{
        bool operator() (vector<int> const &a, vector<int> const &b) {
            return a[2] > b[2];
        }
    };

    int minMaxWeight(int n, vector<vector<int>>& edges, int threshold) {
        // Vector of {end_node, weight}
        map<int, vector<vector<int>>> adj;
        for(auto edge: edges) {
           adj[edge[1]].push_back({edge[0], edge[2]});
        }
        priority_queue<vector<int>,vector<vector<int>>, custsort> pq; // Priority Queue to sort the edges by their weights
        vector<int> thres(n, 0); // Keeps track of threshold for every node
        vector<int> visited(n, 0); // Keeps track if a node is visited or not
        vector<int> connected_nodes; // Vector of all connected nodes
        connected_nodes.push_back(0);
        visited[0] = 1;
        for(auto edge: adj[0]) {
            pq.push({0, edge[0], edge[1]});
        }
        int ans = 0; // Stores our answer
        while(pq.size()>0) {
            if(connected_nodes.size()==n)
                return ans;
            vector<int> t = pq.top(); // temporary vector
            pq.pop();
            // If we have already visited the node or we are crossing the threshold, skip it
            if(visited[t[1]] || thres[t[1]]==threshold)
                continue;
            visited[t[1]] = 1;
            thres[t[1]]++;
            connected_nodes.push_back(t[1]);
            // Compare the weight of the edge added with the current answer
            ans = max(ans, t[2]);
            for(auto edge: adj[t[1]]) {
                pq.push({t[1], edge[0], edge[1]});
            }
        }
        // At the end, if we could not connect all nodes, return -1
        if(pq.size()==0 && connected_nodes.size()<n)
            return -1;
        return ans;
    }
};
```
['Greedy', 'Heap (Priority Queue)', 'Minimum Spanning Tree', 'C++']