#include #pragma GCC optimize("Ofast") #pragma GCC optimize("O3") #pragma GCC optimize("unroll-loops") #pragma GCC target("avx") #include using namespace std; using ll = long long; using vec = vector; using vect = vector; using Graph = vector>; #define endl '\n' #define loop(i, n) for (register int i = 0; i < n; i++) #define Loop(i, m, n) for (int i = m; i < n; i++) #define pool(i, n) for (int i = n; i >= 0; i--) #define Pool(i, m, n) for (int i = n; i >= m; i--) #define modd 1000000007ll // #define modd 998244353ll #define flagcount(bit) __builtin_popcount(bit) #define flag(x) (1ll << x) #define flagadd(bit, x) bit |= flag(x) #define flagpop(bit, x) bit &= ~flag(x) #define flagon(bit, i) bit &flag(i) #define flagoff(bit, i) !(bit & (1ll << i)) #define all(v) v.begin(), v.end() #define low2way(v, x) lower_bound(all(v), x) #define high2way(v, x) upper_bound(all(v), x) #define idx_lower(v, x) (distance(v.begin(), low2way(v, x))) // 配列vでx未満の要素数を返す #define idx_upper(v, x) (distance(v.begin(), high2way(v, x))) // 配列vでx以下の要素数を返す #define idx_lower2(v, x) (v.size() - idx_lower(v, x)) // 配列vでx以上の要素数を返す #define idx_upper2(v, x) (v.size() - idx_upper(v, x)) // 配列vでxより大きい要素の数を返す #define idx_count(v, l, r) max(0, (int)(idx_upper(v, r) - idx_lower(v, l))) // 配列vでl<=x<=rを満たす要素xの数を返す #define putout(a) cout << a << '\n' #define Sum(v) accumulate(all(v), 0ll) ll ctoi(char c) { if (c >= '0' && c <= '9') { return c - '0'; } return -1; } template string make_string(T N) { string ret; T now = N; while (now > 0) { T x = now % 10; ret += (char)('0' + x); now /= 10; } reverse(all(ret)); return ret; } template T gcd(T a, T b) { if (a % b == 0) { return (b); } else { return (gcd(b, a % b)); } } template T lcm(T x, T y) { T z = gcd(x, y); return x * y / z; } template bool primejudge(T n) { if (n < 2) return false; else if (n == 2) return true; else if (n % 2 == 0) return false; double sqrtn = sqrt(n); for (T i = 3; i < sqrtn + 1; i++) { if (n % i == 0) { return false; } i++; } return true; } template bool chmax(T &a, const T &b) { if (a < b) { a = b; // aをbで更新 return true; } return false; } template bool chmin(T &a, const T &b) { if (a > b) { a = b; // aをbで更新 return true; } return false; } // 場合によって使い分ける const ll dx[4] = {1, 0, -1, 0}; const ll dy[4] = {0, 1, 0, -1}; // const ll dx[8] = {1, 1, 0, -1, -1, -1, 0, 1}; // const ll dy[8] = {0, 1, 1, 1, 0, -1, -1, -1}; // cout << fixed << setprecision(10); // vector> field(h, vector(w)); template void putA(vector &A) { int sz = A.size(); for (int i = 0; i < sz; i++) { cout << A[i]; if (i != sz - 1) cout << " "; else cout << "" << endl; } } ll extgcd(ll a, ll b, ll &x, ll &y) { if (b == 0) { x = 1; y = 0; return a; } ll d = extgcd(b, a % b, y, x); y -= a / b * x; return d; } struct union_find { vector par; // 親の番号  vector rank; // 木の深さ(根のランクは0) vector siz; // 要素xが根なら木の頂点数を格納する // 初期化子リストを用いた初期化 union_find(long long N) : par(N), rank(N), siz(N) { for (long long i = 0; i < N; i++) { par[i] = i; rank[i] = 0; siz[i] = 1; } } // 要素xが所属する木の根を再帰的に発見する long long root(long long x) { if (par[x] == x) return x; return par[x] = root(par[x]); // 経路圧縮 } // 要素xが属する木と要素yが属する木の併合 void unite(long long x, long long y) { long long rx = root(x); long long ry = root(y); if (rx == ry) return; // 同じ木に属してたらそのまま if (rank[rx] < rank[ry]) { par[rx] = ry; // 根がryの木に併合 siz[ry] = siz[rx] + siz[ry]; } else { par[ry] = rx; // 根がrxの木に併合 siz[rx] = siz[rx] + siz[ry]; if (rank[rx] == rank[ry]) rank[rx]++; } } // 要素xが属する木と要素yが属する木が同じならtrueを返す bool same(long long x, long long y) { return root(x) == root(y); } // 要素xが属する木の頂点数を返す long long size(long long x) { return siz[root(x)]; } }; int main() { /* どういう問題か Σ{1<=u> N >> M; ll ans = 0; vector> edges(M); loop(i, M) { ll a, b, w; cin >> a >> b >> w; a--; b--; edges[i] = make_tuple(w, a, b); } sort(all(edges)); union_find tree(N); loop(i, M) { ll w = get<0>(edges[i]), a = get<1>(edges[i]), b = get<2>(edges[i]); if (tree.same(a, b)) continue; ans += tree.size(a) * tree.size(b) * w; tree.unite(a, b); } putout(ans); return 0; }