/*
* Problem :: 1197 / 최소 스패닝 트리
*
* Kind :: Graph
*
* Insight
* - 최소 스패닝 트리(MST, Minimum Spanning Tree)
* MST 간선의 개수 = MST 정점의 개수 - 1
* + Kruskal 알고리즘으로 구하자
* # 사이클 확인은 Union-Find 알고리즘으로 해결하자
* -> 사이클이 만들어지려면
* 같은 집합에 속한 간선을 추가해야 하는데
* 이 경우 Find 알고리즘으로 막을 수 있다
*
* Point
* - 다음에 만나는 최소 스패닝 트리 문제는
* Prim 알고리즘으로 해결하자
* + 왜 지금 안하세요?
* # 귀찮아서...
*/
//
// BOJ
// ver.C++
//
// Created by GGlifer
//
// Open Source
#include <iostream>
#include <vector>
#include <queue>
#include <tuple>
#include <algorithm>
using namespace std;
#define endl '\n'
// Set up : Global Variables
struct Edge { int a, b, c; };
int P[10000+1], R[10000+1];
// Set up : Functions Declaration
int find(int x);
void merge(int x, int y);
bool operator > (Edge u, Edge v);
int main()
{
// Set up : I/O
ios::sync_with_stdio(false);
cin.tie(nullptr);
// Set up : Input
int V, E;
cin >> V >> E;
priority_queue<Edge, vector<Edge>, greater<>> pq;
for (int i=0; i<E; i++) {
int A, B, C;
cin >> A >> B >> C;
pq.push({A, B, C});
}
// Process
/* Union-Find 알고리즘을 위한 전처리 */
for (int i=1; i<=V; i++) {
P[i] = i;
R[i] = 1;
}
/* cnt = 현재 MST 간선의 개수
* cnt 가 V-1 이면 모든 정점이 연결되었다는 것을 뜻함
* 즉, MST 가 완성되었음을 가리킴 */
int cnt = 0, ans = 0;
while (cnt < V-1) {
/* 우선순위 큐에 남은 간선들 중 가중치가 가장 작은 간선 추출 */
auto [A, B, C] = pq.top();
pq.pop();
/* 서로 이미 연결된 정점이면 넘어감 */
if (find(A) == find(B)) continue;
merge(A, B); /* 정점 연결 */
ans += C; /* 가중치 증가 */
cnt++; /* 간선 개수 증가 */
}
// Control : Output
cout << ans << endl;
}
// Helper Functions
bool operator > (Edge u, Edge v)
/* 구조체 Edge 비교를 위한 연산자 오버로딩 */
{
return make_tuple(u.c, u.b, u.a) > make_tuple(v.c, v.b, v.a);
}
int find(int x)
/* Union-Find 알고리즘의 Find 함수 */
{
if (P[x] == x) return x;
return P[x] = find(P[x]);
}
void merge(int x, int y)
/* Union-Find 알고리즘의 Union 함수 */
{
x = find(x);
y = find(y);
if (x == y) return;
if (R[x] < R[y]) swap(x, y);
P[y] = x;
if (R[x] == R[y]) R[x]++;
}