// Slower C++ solution:
#include <iostream>

#include <algorithm>

#include <vector>

#include <queue>

using namespace std;

typedef long long  ll;

#define rep(i,s,n)for(ll i=s;i<n;i++)

#define repe(i,s,n)for(ll i=s;i<=n;i++)



static const ll MOD = 1e9 + 7;

static const ll INF = (ll)1 << 62;



static const ll MAX_N = 100001;

static const ll MAX_M = 200001;



ll V, E;

struct edge {

	ll  to, cost;

};

//edge es[MAX_M];

ll d[MAX_N];

//void bellmanford(ll s) {

//	fill(d, d + V, INF);

//	d[s] = 0;

//	ll cnt = 0;

//	while (1) {

//		cnt++;

//		bool update = false;

//		rep(i, 0, E) {

//			edge e = es[i];

//			if (d[e.from] != INF && d[e.to] > d[e.from] + e.cost) {

//				d[e.to] = d[e.from] + e.cost;

//				if (cnt == V - 1) {

//					cout << "NEGATIVE CYCLE" << endl;

//					exit(0);

//				}

//				update = true;

//			}

//		}

//		if (!update) break;

//	}

//}

vector<edge> G[MAX_N];

typedef pair<ll, ll> P;

priority_queue<P, vector<P>, greater<P>> que;



void dijkstra(ll s) {

	fill(d, d + V, INF);

	d[s] = 0;

	que.push(P(0, s));



	while (!que.empty()) {

		P p = que.top(); que.pop();

		ll v = p.second;

		if (p.first > d[v])continue;

		rep(i, 0, G[v].size()) {

			edge e = G[v][i];

			if (d[e.to] > d[v] + e.cost) {

				d[e.to] = d[v] + e.cost;

				que.push(P(d[e.to], e.to));

			}

		}

	}

}

bool mst[MAX_N];

ll cost = 0;

bool complete() {

	bool complete = true;

	rep(i, 0, V) complete &= mst[i];

	return complete;

}

void prim(ll s) {

	fill(mst, mst + V, false);

	que.push(P(0, s));

	while (!que.empty()) {

		P p = que.top(); que.pop();

		ll v = p.second;

		if (mst[v]) continue;



		cost += p.first;

		mst[v] = true;

		rep(i, 0, G[v].size()) {

			edge e = G[v][i];

			if (!mst[e.to]) {

				que.push(P(e.cost, e.to));

			}

		}

		if (complete())break;

	}

}



int main() {

	//ll r; cin >> V >> E >> r;

	//rep(i, 0, E) {

	//	ll s, t, d; cin >> s >> t >> d;

	//	G[s].push_back(edge{ t, d });

	//	//G[t].push_back(edge{ s, d });

	//}

	//dijkstra(r);



	//rep(i, 0, E) {

	//	ll s, t, d; cin >> s >> t >> d;

	//	es[i].from = s;

	//	es[i].to = t;

	//	es[i].cost = d;

	//}

	//bellmanford(r);



	//rep(i, 0, V) {

	//	if (d[i] == INF)cout << "INF" << endl;

	//	else cout << d[i] << endl;

	//}



	cin >> V >> E;

	rep(i, 0, E) {

		ll s, t, d; cin >> s >> t >> d;

		G[s].push_back(edge{ t, d });

		G[t].push_back(edge{ s, d });

	}

	prim(0);



	cout << cost << endl;



	return 0;

}


// Faster C++ solution for the same problem (measured speedup: 3.5258203006679514x):
#include <iostream>

#include <algorithm>

#include <vector>

#include <set>

#include <map>

#include <queue>

using namespace std;

#define rep(i,s,n)for(int i = s;i<n;i++)

#define repe(i,s,n)for(int i = s;i<=n;i++)

typedef long long ll;

static const ll MOD = 1e9 + 7;

static const ll INF = (ll)1 << 62;



//////////////////////////////////////////////////////

//Union Find /////////////////////////////////////////

//////////////////////////////////////////////////////

static const ll MAX_M = 100001;

static const ll MAX_N = 100001;

int par[MAX_N];

int rak[MAX_N];



void init(int n) {

	rep(i, 0, n) {

		par[i] = i;

		rak[i] = 0;

	}

}



int find(int x) {

	if (par[x] == x)return x;

	else return par[x] = find(par[x]);

}



void unite(int x, int y) {

	x = find(x);

	y = find(y);

	if (x == y)return;



	if (rak[x] < rak[y]) par[x] = y;

	else {

		par[x] = y;

		if (rak[x] == rak[y]) rak[x]++;

	}

}



bool same(int x, int y) {

	return find(x) == find(y);

}



struct edge { int u, v, cost; };

bool comp(const edge& e1, const edge& e2) {

	return e1.cost < e2.cost;

}



int V, E;

edge es[MAX_M];



int krascal() {

	sort(es, es + E, comp);

	init(V);

	int res = 0;



	rep(i, 0, E) {

		edge e = es[i];

		if (!same(e.u, e.v)) {

			unite(e.u, e.v);

			res += e.cost;

		}

	}

	return res;

}



int main() {

	ll n, m; cin >> n >> m;

	V = n, E = m;

	rep(i, 0, m) {

		ll s, t, w; cin >> s >> t >> w;

		edge *e = new edge();

		e->u = s;

		e->v = t;

		e->cost = w;

		es[i] = *e;

	}



	cout << krascal() << endl;



	return 0;

}