//Kruskal算法按照边的权值从小到大查看一遍,如果不产生圈(重边等也算在内),就把当前这条表加入到生成树中。
//如果判断是否产生圈。假设现在要把连接顶点u和顶点v的边e加入生成树中。如果加入之前的u和v不在同一个连通分量里,那么加入e也不会产生圈。
//反之,如果u和v在同一个连通分量里,那一定会产生圈。可以用并查集高效判断是否属于同一个连通分量。
#define _CRT_SECURE_NO_WARNINGS
/*
7 10
0 1 5
0 2 2
1 2 4
1 3 2
2 3 6
2 4 10
3 5 1
4 5 3
4 6 5
5 6 9
*/
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std; const int maxn = + ;
const int INF = ;
int par[maxn]; //父亲, 当par[x] = x时,x是所在的树的根
int Rank[maxn]; //树的高度
struct edge
{
int u, v, cost;
}; bool comp(const edge& e1, const edge& e2) {
return e1.cost < e2.cost;
} edge es[maxn];
int V, E; //顶点数和边数
//并查集实现-高效的判断是否属于同一个连通分量。
void init_union_find(int n);
int find(int x);
void unite(int x, int y);
bool same(int x, int y);
void init();
void input();
int kruskal(); //最小生成树算法 //初始化n个元素
void init_union_find(int n)
{
for (int i = ; i < n; i++) {
par[i] = i;
Rank[i] = ;
}
} //查询树的根
int find(int x) {
if (par[x] == x) {
return x;
}
else {
return par[x] = find(par[x]);
}
} //合并x和y所属集合
void unite(int x, int y) {
x = find(x);
y = find(y);
if (x == y) return; if (Rank[x] < Rank[y]) {
par[x] = y;
}
else {
par[y] = x;
if (Rank[x] == Rank[y]) Rank[x]++; //如果x,y的树高相同,就让x的树高+1
}
} //判断x和y是否属于同一个集合
bool same(int x, int y) {
return find(x) == find(y);
} void init() { } void input()
{
edge tmp;
for (int i = ; i < E; i++) {
cin >> tmp.u >> tmp.v >> tmp.cost;
es[i] = tmp;
}
} int kruskal()
{
sort(es, es + E, comp); //按照edge.cost的顺序从小到大排序
init_union_find(V); //将并查集初始化
int res = ;
for (int i = ; i < E; i++) {
edge e = es[i];
if (!same(e.u, e.v)) {
unite(e.u, e.v);
res += e.cost;
}
}
return res;
} int main()
{
cin >> V >> E;
input();
int res = kruskal();
cout << res << endl;
return ;
}