#include <algorithm>
class Solution {
  public:
    /**
     n 户人家 → n 个点(顶点)
     m 条路 → m 条边,每条边有一个权重(修路成本)
     要让所有人家连起来(形成连通图),而且总成本最低 → 最小生成树(Minimum Spanning Tree, MST)
	 
     生成树:包含所有顶点,且n-1条边,也就是一条线连接所有顶点;
	 最小生成树:采用的边的代价和最小
	
     Kruskal 的核心思想是一个贪心策略:
        每次挑最便宜的路(按边的距离排序),利用并查集, 如果两个节点还没有连接(不在一个集合里),就修它(放到一个集合,累计成本),直到所有村庄连通(n-1个边被使用)。
     */
    struct UnionFind{
        vector<int> parent, rank;

        UnionFind(int n){
            parent.resize(n);    //对头节点进行初始化
            rank.resize(n, 0);   //对集合的高度进行初始化

            for(int i=0; i<n; i++){
                parent[i] = i;   //每个的头节点初始化为自己,表示自己是一个集合
            }
        }

        int find(int x){  //查找数x的头节点是多少,并进行路径压缩(找到后赋值)
            if(parent[x] != x){  //若是下属
                parent[x] = find(parent[x]);  //如果 
            }
            return parent[x];
        }

        // 合并两个集合
        bool unite(int x, int y){
            int root_x = find(x);
            int root_y = find(y);

            if(root_x == root_y)return false;  //两个城市已经在同一个集合里了,不需要合并

            // 把树高度低的那部分,放到高的集合下
            if(rank[root_x] < rank[root_y]){
                parent[root_x] = root_y;  //改变头节点即可
            }else if(rank[root_y] < rank[root_x]){
                parent[root_y] = root_x;
            }else{
                parent[root_y] = root_x;  //一样高时,作为两个集合的头的高度+1;
                rank[root_x]++;
            }

            return true;
        }
    };

    int miniSpanningTree(int n, int m, vector<vector<int> >& cost) {
        sort(cost.begin(), cost.end(), [](vector<int>& a, vector<int>& b){
            return a[2] < b[2];
        });  //按照成本从小到大排序

        UnionFind uf(n);  //创建并查集

        int totalCost = 0;  //答案:总长度
        int edgeUsed = 0;  //记录已经采用的边,到n-1时停止

        //  从最短边开始遍历,尝试不同集合
        for(int i=0; i<m; i++){
            int u = cost[i][0]-1;  //头节点,cost里的节点是从1开始的,而我的并查集是从0开始的
            int v = cost[i][1]-1;  //尾
            int w = cost[i][2];  //代价

            if(uf.unite(u, v)){
                totalCost += w; 
                edgeUsed ++;
                if( edgeUsed == n-1){
                    break;
                }
            }
        }

        return edgeUsed==n-1 ? totalCost:-1;
    }
};