#include <stdio.h>
#include <assert.h>

namespace beet{
  
  template<typename T>
  void swap(T &a,T &b){
    T x=a;
    a=b;
    b=x;
  }
  
  template<typename T>
  struct Vector{
    int sz,cap;
    T *data;
    Vector():sz(0),cap(1){
      data = new T[cap];
    }    
    Vector(int sz):sz(sz),cap(sz<<1){
      data = new T[cap];
    }
    Vector(int sz,T t):sz(sz),cap(sz<<1){
      data = new T[cap];
      for(int i=0;i<sz;i++) data[i]=t;
    }
    ~Vector(){
      delete[] data;
    }
    void resolve(){
      T *tmp = new T[cap];
      for(int i=0;i<sz;i++) tmp[i]=data[i];
      delete[] data;
      data=tmp;
    }
    void resize(int nsz){
      cap=nsz<<1;
      if(sz>cap) sz=cap;
      resolve();      
    }
    void assign(int nsz,T t){
      resize(nsz);
      sz=nsz;
      for(int i=0;i<sz;i++) data[i]=t;
    }    
    void push_back(T x){
      if(sz>=cap){
        cap<<=1;
        assert(sz<cap);
        resolve();
      }      
      data[sz++]=x;
    }
    void pop_back(){
      sz--;
    }
    int size() const{
      return sz;
    }
    const T operator[](int x) const{
      return data[x];
    }
    T& operator[](int x){
      assert(0<=x&&x<sz);
      return data[x];
    }
  };  
  
  template<typename T>
  void sort(Vector<T> &vs){
    if(vs.size()==0) return;
    
    int n=vs.size();
    Vector<T> tmp(n);
    
    auto dfs=
      [&](int l,int r,auto &func)->void{
        // printf("%d %d\n",l,r);
        if(l+1==r) return;
        int m=(l+r)>>1;
        func(l,m,func);
        func(m,r,func);        
        for(int k=l;k<r;k++) tmp[k]=vs[k];

        int i=l,j=m,k=l;
        while(i<m&&j<r){
          if(tmp[i]<tmp[j]){
            vs[k++]=tmp[i++];
          }else{
            vs[k++]=tmp[j++];
          }
        }
        while(i<m) vs[k++]=tmp[i++];
        while(j<r) vs[k++]=tmp[j++];
      };
    dfs(0,n,dfs);
  }
  

  template<typename T>
  struct Kruskal{
  
    struct edge{
      int from,to;
      T cost;
      int used;
      edge(){}     
      edge(int from,int to,T cost):
        from(from),to(to),cost(cost),used(0){}
      bool operator<(const edge& e) const{
        return cost<e.cost;
      }
    };

    int n;
    Vector<int> r,p;
    Vector<edge> es;

    Kruskal(){}
    Kruskal(int n):n(n),r(n,1),p(n){
      for(int i=0;i<n;i++) p[i]=i;     
    }
  
    int find(int x){    
      return (x==p[x]?x:p[x]=find(p[x]));
    }

    bool same(int x,int y){
      return find(x)==find(y);
    }

    void unite(int x,int y){
      x=find(x);y=find(y);
      if(x==y) return;
      if(r[x]<r[y]) swap(x,y);
      r[x]+=r[y];
      p[y]=x;
    }

    void add_edge(int u,int v,T c){
      es.push_back(edge(u,v,c));
    }
  
    T build(){
      sort(es);
      T res=0;
      for(int i=0;i<(int)es.size();i++){
        auto &e=es[i];
        if(!same(e.from,e.to)){
          res+=e.cost;
          unite(e.from,e.to);
          e.used=1;
        }
      }
      return res;
    }
  };

}

//INSERT ABOVE HERE
using namespace beet;

int main(){
  int n,m;
  scanf("%d %d",&n,&m);

  Kruskal<int> mst(n);
  for(int i=0;i<m;i++){
    int s,t,w;
    scanf("%d %d %d",&s,&t,&w);
    mst.add_edge(s,t,w); 
  }
  
  printf("%d\n",mst.build());  
  return 0;  
}



Measured memory_bytes: 5536