#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;
//此题有坑点:可能有多条不同权值的重边,因为这道题无负权,所以不需要标记,也不可以标记
//解题思路:jijkstra裸题,有所不同的是还需要沿另一条路径回来而不是原路返回,则需要反向建图,得到返回时各点距离
const int maxn = 1e3 + 5;
const int maxm = 1e5 + 5;
inline int read()
{
register int x = 0, f = 1;
register char c = getchar();
while(c > '9' || c < '0')
{
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9')
{
x = (x << 3) + (x << 1) + (c ^ 48);
c = getchar();
}
return x * f;
}
struct edge
{
int v, w;
};
//用的邻接表存图,其实这道题用邻接矩阵会更快也更方便些
//链式前向星存图当然也可以,但是感觉写起来稍麻烦
vector<edge> zp[maxn];
vector<edge> fp[maxn];
struct node
{
int pos, dis;
// 重载运算符
bool operator<(const node &rhs) const
{
// >时是最小堆,反之则为最大堆
return dis > rhs.dis;
}
};
//堆优化,查找最小距离点,降低时间复杂度
priority_queue<node> q;
int dis[maxn];
void dijkstra(int u, const vector<edge> *mp)
{
memset(dis, 0x3f, sizeof(dis));
q.push((node){u, 0});
dis[u] = 0;
while(!q.empty())
{
u = q.top().pos;
int tdis = q.top().dis;
// cout << tdis << " ";
q.pop();
for(int v, w, i = 0; i < mp[u].size(); i++)
{
v = mp[u][i].v;
w = mp[u][i].w;
if(tdis + w < dis[v])
{
dis[v] = tdis + w;
q.push((node){v, dis[v]});
// 这道题不需要标记,因为会有重边
// mark[v] = true;
}
}
}
}
int main()
{
int n = read(), m = read();
for(int u, v, w, i = 1; i <= m; i++)
{
u = read(); v = read(); w = read();
zp[u].push_back((edge){v, w});
fp[v].push_back((edge){u, w});
}
// 出发距离和
dijkstra(1, zp);
long long ans(0);
for(int i = 2; i <= n; i++)
{
ans += dis[i];
}
// 返回距离和
dijkstra(1, fp);
for(int i = 2; i <= n; i++)
{
ans += dis[i];
}
printf("%lld\n", ans);
return 0;
}