#include <bits/stdc++.h>
#define _GLIBCXX_DEBUG
using namespace std;
#define rep(i, n) for (int i = 0; i < (int)(n); i++)
#define rep2(i, s, n) for (int i = s; i < (int)(n); i++)
#define Clear(a) a = decltype(a)()
#define all(a) a.begin(),a.end()
#define vec vector
typedef long long ll;
typedef pair<ll,ll> P;
//const ll big=998244353;
const ll big=1000000007LL;
const ll INF=1000000000000000000LL;//1e18
const int dx[4]={1,0,-1,0};
const int dy[4]={0,1,0,-1};
ll max(ll x,ll y){
if(x>y)return x;
else return y;
}
ll min(ll x,ll y){
if(x<y)return x;
else return y;
}
ll expm(ll x,ll y){
    if(y==0)return 1;//0^0=1
    if(x==1||x==0)return x;
    if(y%2==1)return (expm(x,y-1)*x)%big;
    ll t=expm(x,y/2);
    return (t*t)%big;
}
ll exp(ll x,ll y){
    if(y==0)return 1;//0^0=1
    if(x==1||y==0)return x;
    if(y%2==1)return exp(x,y-1)*x;
    ll t=exp(x,y/2);
    return t*t;
}
ll inv(ll x){
    return expm(x,big-2);
}

struct unionfind{
    vector<int> par;
    vector<int> size;
    int num;
    unionfind(int n):num(n), par(n,-1), size(n,1){}
    int find(const int x){
        if(par[x]==-1)return x;
        return par[x]=find(par[x]);
    }
    void unite(int x,int y){
        x=find(x);
        y=find(y);
        if(x==y)return;
        if(size[x]>size[y])swap(x,y);
        if(size[x]==size[y])size[y]++;
        par[x]=y;
        num--;
        return;
    }
    void get(const int m){
        rep(i,m){
            int a,b;cin >> a >> b;
            a--;b--;
            unite(a,b);
        }
        return;
    }
};

struct edge{
    ll from,to,cost;
    bool operator==(const edge a)const{
        if((*this).from==a.from&&(*this).to==a.to&&(*this).cost==a.cost)
            return true;
        else return false;
    }
    bool operator!=(const edge a)const{
        return !((*this)==a);
    }
    bool operator>(const edge a)const{
        if((*this).cost>a.cost)return true;
        else return false;
    }
    bool operator<(const edge a)const{
        if((!((*this)>a))&&(!((*this)==a)))return true;
        else return false;
    }
    bool operator<=(const edge a)const{
        return !((*this)>a);
    }
    bool operator>=(const edge a)const{
        return !((*this)<a);
    }
};

//setにedgeたちを渡す nは頂点数
vec<vec<P>> kruskal(priority_queue<edge,vec<edge>,greater<edge>> &a,int n){
    unionfind uf(n);
    vec<vec<P>> ans(n);
    while(!a.empty()){
        edge k=a.top();
        a.pop();
        if(uf.find(k.from)==uf.find(k.to))continue;
        else{
            ans[k.from].emplace_back(P(k.to,k.cost));
            ans[k.to].emplace_back(P(k.from,k.cost));
            uf.unite(k.to,k.from);
        }
        rep(i,n-1){
            if(uf.find(i)!=uf.find(i+1))goto skip;
        }
        return ans;
        skip:;
    }
    return ans;
}

int main(){
    ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    cout << fixed << setprecision(10);
    /*--------------------------------*/

    int v,e;cin >> v >> e;
    priority_queue<edge,vec<edge>,greater<edge>> a;
    rep(i,e){
        int s,t,w;cin >> s >> t >> w;
        edge k;k.from=s,k.to=t,k.cost=w;
        a.push(k);
    }
    
    vec<vec<P>> ans=kruskal(a,v);
    ll res=0;
    rep(i,v)rep(j,ans[i].size()){
        res+=ans[i][j].second;
    }
    cout << res/2 << endl;
}


Measured memory_bytes: 5940