#include <algorithm>
#include <array>
#include <cassert>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <limits>
#include <map>
#include <numeric>
#include <queue>
#include <sstream>
#include <stack>
#include <string>
#include <utility>
#include <vector>
#define FOR(i,a,b) for (int i=(a),for_##i##_max=(b);i<=(for_##i##_max);++i)
#define RFOR(i,a,b) for (int i=(a),rfor_##i##_min=(b);i>=(rfor_##i##_min);--i)
#define REP(i,n) for (int i=0,rep_##i##_len=(n);i<(rep_##i##_len);++i)
#define RREP(i,n) for (int i=(n)-1;i>=0;--i)

namespace { // Basics

template<typename T>
void chmin(T &x, T y) {
  x = std::min(x, y);
}

template<typename T>
void chmax(T &x, T y) {
  x = std::max(x, y);
}

template<typename T>
__attribute__((unused))
const auto minimum = static_cast<T const &(*)(T const &, T const &)>(std::min);

template<typename T>
__attribute__((unused))
const auto maximum = static_cast<T const &(*)(T const &, T const &)>(std::max);

template<typename T>
__attribute__((unused))
T constexpr infty = std::numeric_limits<T>::max / 3;
template<>
__attribute__((unused))
auto constexpr infty<int> = 1'100'100'100;
template<>
__attribute__((unused))
auto constexpr infty<long long int> = 100'500'400'300'200'100LL;

using lli = long long int;
using ld = long double;

} // namespace
namespace { // Graph Basics

template<typename WeightT>
struct edge {
  int from, to;
  WeightT weight;
  edge(int s, int d, WeightT w): from(s), to(d), weight(w) {}
};

template<typename WeightT>
bool
operator< (edge<WeightT> const &lhs, edge<WeightT> const &rhs) {
  if (lhs.weight != rhs.weight) {
    return lhs.weight < rhs.weight;
  } else if (lhs.from != rhs.from) {
    return lhs.from < rhs.from;
  }
  return lhs.to < rhs.to;
}

template<typename WeightT>
using edges = std::vector<edge<WeightT> >;

template<typename WeightT>
using adjacency_list = std::vector<edges<WeightT> >;

template<typename WeightT>
void
add_undir_edge(adjacency_list<WeightT> &G, int u, int v, WeightT w) {
  G[u].emplace_back(u, v, w);
  G[v].emplace_back(v, u, w);
}

template<typename WeightT>
adjacency_list<WeightT>
inverse_graph(adjacency_list<WeightT> const &G) {
  size_t const n = G.size();
  adjacency_list<WeightT> F(n);
  for (auto &&es: G) for (auto &&e: es) {
    F[e.to].emplace_back(e.to, e.from, e.weight);
  }
  return F;
}

template <typename WeightT>
edges<WeightT>
all_edges(adjacency_list<WeightT> const &G) {
  auto const E = std::accumulate(begin(G), end(G), 0,
      [](auto x, auto vec) { return x + vec.size(); });
  edges<WeightT> es;
  es.reserve(E);
  for (auto &&vec: G) {
    std::copy(begin(vec), end(vec), std::back_inserter(es));
  }
  return es;
}

} // namespace
class union_find_tree {
  public:
    explicit union_find_tree(size_t n): par(n), rank(n, 0) {
      for (size_t i = 0; i < n; ++i) { par[i] = i; }
    }

    size_t find(size_t i) {
      if (par[i] == i) {
        return i;
      } else {
        return par[i] = find(par[i]);
      }
    }

    void unite(size_t i, size_t j) {
      i = find(i), j = find(j);
      if (i == j) { return; }

      if (rank[i] < rank[j]) {
        par[i] = j;
      } else {
        par[j] = i;
        if ((rank[i] = rank[j])) { rank[i]++; }
      }
    }

    bool same(size_t i, size_t j) {
      return find(i) == find(j);
    }
  private:
    std::vector<size_t> par;
    std::vector<size_t> rank;
};
namespace { // Kruskal

template <typename WeightT>
struct kruskal_result_t {
  WeightT cost;
  edges<WeightT> branches;
};

template <typename WeightT>
kruskal_result_t<WeightT>
kruskal(adjacency_list<WeightT> const &G) {
  auto es = all_edges(G);
  std::sort(begin(es), end(es));

  WeightT cost = 0;
  edges<WeightT> branches;

  union_find_tree U(G.size());
  for (auto &&e: es) {
    if (!U.same(e.from, e.to)) {
      U.unite(e.from, e.to);
      cost += e.weight;
      branches.push_back(e);
    }
  }
  return { cost, branches };
}

} // namespace

using namespace std;

using Graph = adjacency_list<lli>;

void solve(Graph const &G) {
  auto const res = kruskal(G);
  cout << res.cost << endl;
}

int main() {
  // cout << fixed << setprecision(10);

  int N, M; cin >> N >> M;
  Graph G(N);
  REP(i, M) {
    lli S, T, W; cin >> S >> T >> W;
    add_undir_edge(G, S, T, W);
  }
  solve(G);
  return 0;
}



Measured memory_bytes: 11924