#define _USE_MATH_DEFINES
#include<iostream>
#include<algorithm>
#include<string>
#include<iomanip>
#include<cmath>
#include<vector>
#include<deque>
#include<queue>
#include<map>
#include<set>
#define ll long long
#define str string
#define ld long double
#define vec vector
#define vll vec<ll>
#define rep(i,a,b) for(ll i=a;i<b;i++)
#define rrep(i,b,a) for(ll i=b;i>=a;i--)
#define ALL(a) a.begin(),a.end()
#define rALL(a) a.rbegin(),a.rend()
#define kai "\n"
#define yuque priority_queue
#define pub push_back
#define pob pop_back
#define puf push_front
#define pof pop_front
#define mie min_element
#define mae max_element
#define tos to_string
#define sep setprecision
#define lob lower_bound
#define upb upper_bound
#define nep next_permutation
#define MOD 1000000007ll
#define MIN (1e-10)
#define equal(a,b) (abs((a)-(b))<MIN)
#define v2 ten
#define chokusen senbun
#define kakkei vec<ten>
using namespace std;
void solve();

struct pea {
	ll f, s;
	bool operator<(const pea &p)const {
		return f != p.f ? f < p.f : s < p.s;
	};
};
struct tri {
	ll f, s, t;
	bool operator<(const tri &p)const { return f < p.f; };
};
class ten {
public:
	ld x, y;

	ten operator+(ten p) { return { x + p.x,y + p.y }; }
	ten operator-(ten p) { return { x - p.x,y - p.y }; }
	ten operator*(ld k) { return { k*x,k*y }; }
	ten operator/(ld k) { return { x / k,y / k }; }

	ld abs() { return sqrt(abs2()); }
	ld abs2() { return x * x + y * y; };

	bool operator<(const ten &p)const { return x != p.x ? x < p.x : y < p.y; };
	bool operator==(const ten &p)const {
		return (-MIN < x - p.x) && (x - p.x < MIN) && (-MIN < y - p.y) && (y - p.y < MIN);
	};

	ld naiseki(v2 b) { return x*b.x + y*b.y; }
	ld gaiseki(v2 b) { return x*b.y - y*b.x; }
};
struct senbun { ten p1, p2; };
struct en { ten o; ld r; };

//互いに素な集合
class ufset {
public:
	vll rank, p;
	ufset(ll n) {
		rank.resize(n, 0);
		p.resize(n, 0);
		rep(i, 0, n)make(i);
	}
	void reset(ll n) {
		rank.resize(n, 0);
		p.resize(n, 0);
		rep(i, 0, n)make(i);
	}
	void make(ll i) {
		p[i] = i;
		rank[i] = 0;
	}
	bool same(ll i, ll j) { return find(i) == find(j); }
	void unite(ll i, ll j) { link(find(i), find(j)); }
	void link(ll i, ll j) {
		if (rank[i] > rank[j])p[j] = i;
		else {
			p[i] = j;
			if (rank[i] == rank[j])rank[j]++;
		}
	}
	ll find(ll i) {
		if (p[i] != i)p[i] = find(p[i]);
		return p[i];
	}
};

//nCk(mod p)
class nck {
public:
	ll NUM, p;
	vll fac, finv, inv;
	nck(ll maxn, ll mod = MOD) {
		NUM = maxn + 1;
		p = mod;
		fac.resize(NUM);
		finv.resize(NUM);
		inv.resize(NUM);
		fac[0] = fac[1] = 1;
		finv[0] = finv[1] = 1;
		inv[1] = 1;
		rep(i, 2, NUM) {
			fac[i] = fac[i - 1] * i%p;
			inv[i] = p - inv[p%i] * (p / i) % p;
			finv[i] = finv[i - 1] * inv[i] % p;
		}
	}
	ll c(ll n, ll k) { return fac[n] * (finv[k] * finv[n - k] % p) % p; }
};

//最大公約数
ll gcd(ll n, ll m) {
	ll k = n % m;
	while (k != 0) { n = m; m = k; k = n % m; }
	return m;
}
ll gcd(vector<ll> a) {
	rep(i, 1, a.size()) {
		ll k = a[i - 1] % a[i];
		while (k != 0) {
			a[i - 1] = a[i];
			a[i] = k;
			k = a[i - 1] % a[i];
		}
	}
	return a.back();
}

//最小公倍数
ll lcm(ll n, ll m) {
	ll t = n * m, k = n % m;
	while (k != 0) { n = m; m = k; k = n % m; }
	return t / m;
}
ll lcm(vector<ll> a) {
	ll n;
	rep(i, 1, a.size()) {
		n = a[i - 1] * a[i];
		ll k = a[i - 1] % a[i];
		while (k != 0) {
			a[i - 1] = a[i];
			a[i] = k;
			k = a[i - 1] % a[i];
		}
		n /= a[i];
		a[i] = n;
	}
	return n;
}

//a^b(mod p)
ll pow2(ll a, ll b, ll p = MOD) {
	ll res = 1;
	if (b > 0) {
		res = pow2(a, b / 2);
		if (b % 2 == 0)res = res * res%p;
		else res = res * res%p*a%p;
	}
	return res;
}

//b^(-1)(mod p)
ll m1j(ll b, ll p = MOD) { return pow2(b, p - 2); }

//トポロジカルソート
void tsort(const vec<vll> &c, vll &a) {
	ll n = c.size();
	vll p(n, 0);
	rep(i, 0, n)rep(j, 0, c[i].size())p[c[i][j]]++;
	rep(i, 0, n)if (p[i] == 0) {
		deque<ll> d; d.pub(i);
		while (!d.empty()) {
			ll j = d.front(); d.pof();
			a.pub(j);
			rep(k, 0, c[j].size()) {
				ll l = c[j][k];
				p[l]--;
				if (p[l] == 0) {
					p[l] = -1;
					d.pub(l);
				}
			}
		}
	}
}

//深さ優先探索
vec<bool> seen;
void dfs(const vec<vll> &c, ll i = 0) {
	seen[i] = true;
	rep(j, 0, c[i].size()) {
		if (!seen[c[i][j]])dfs(c, c[i][j]);
	}
}

//最小全域木
ll prim(vec<vec<pea>> &c) {
	ll sum = 0;
	ll n = c.size();
	vec<bool> seen(n, false);

	ll count = 1;
	seen[0] = true;
	yuque<pea> hen;
	rep(i, 0, c[0].size())hen.push({ -c[0][i].f,c[0][i].s });

	while (count != n) {
		pea k = hen.top(); hen.pop();
		while (seen[k.s]) {
			k = hen.top();
			hen.pop();
		}

		sum -= k.f;
		count++;
		seen[k.s] = true;
		rep(i, 0, c[k.s].size())hen.push({ -c[k.s][i].f,c[k.s][i].s });
	}

	return sum;
}
ll kruskal(ll v, vec<tri> &e) {
	ll sum = 0;
	sort(ALL(e));
	ufset s(v);
	rep(i, 0, e.size()) {
		if (!s.same(e[i].s, e[i].t)) {
			s.unite(e[i].s, e[i].t);
			sum += e[i].f;
		}
	}
	return sum;
}

int main() {
	cin.tie(0);
	ios::sync_with_stdio(false);
	cout << fixed;
	solve();
}
void solve() {
	ll v, e; cin >> v >> e;
	vec<tri> c;
	rep(i, 0, e) {
		ll s, t, w;
		cin >> s >> t >> w;
		c.pub({ w,s,t });
	}
	cout << kruskal(v, c) << kai;
}


Measured memory_bytes: 5952