#include <bits/stdc++.h>
using namespace std;
template <typename T>
struct edge {
  int src, to;
  T cost;
  edge(int to, T cost) : src(-1), to(to), cost(cost) {}
  edge(int src, int to, T cost) : src(src), to(to), cost(cost) {}
  edge &operator=(const int &x) {
    to = x;
    return *this;
  }
  operator int() const { return to; }
};
template <typename T>
using Edges = vector<edge<T>>;
template <typename T>
using WeightedGraph = vector<Edges<T>>;
using UnWeightedGraph = vector<vector<int>>;
template <typename T>
using Matrix = vector<vector<T>>;
const long long inf = 1LL << 60;
struct UnionFind {
  vector<int> data;
  UnionFind(int n) { init(n); }
  void init(int n = 1) { data.assign(n, -1); }
  int root(int x) {
    if (data[x] < 0) return x;
    return (data[x] = root(data[x]));
  }
  bool issame(int x, int y) { return (root(x) == root(y)); }
  bool merge(int x, int y) {
    x = root(x);
    y = root(y);
    if (x == y) return false;
    if (data[x] > data[y]) swap(x, y);
    data[x] += data[y];
    data[y] = x;
    return true;
  }
  int size(int x) { return (-data[root(x)]); }
};
template <typename T>
T kruskal(Edges<T> es, int n) {
  sort(es.begin(), es.end(),
       [](const edge<T> &a, const edge<T> &b) { return (a.cost < b.cost); });
  UnionFind tree(n);
  T ret = 0;
  for (auto e : es) {
    if (tree.merge(e.src, e.to)) ret += e.cost;
  }
  return ret;
}
int main() {
  long long n, m;
  cin >> n >> m;
  Edges<long long> es;
  for (int i = 0; i < m; i++) {
    long long a, b, c;
    cin >> a >> b >> c;
    a--;
    b--;
    es.push_back(edge<long long>(a, b, c));
  }
  long long k = kruskal(es, n);
  cout << m - k << "\n";
  return 0;
}
