#include <bits/stdc++.h>
using namespace std;
using ll = long long;
constexpr int inf = 1e9 + 7;
constexpr ll infll = 1ll << 60ll;
constexpr ll mod = 1e9 + 7;
constexpr int dx[] = {1, 0, -1, 0, 1, 1, -1, -1};
constexpr int dy[] = {0, -1, 0, 1, 1, -1, -1, 1};
namespace {
template <typename T>
bool chmax(T& a, T b) {
  if (a < b) {
    a = b;
    return true;
  }
  return false;
}
template <typename T>
bool chmin(T& a, T b) {
  if (a > b) {
    a = b;
    return true;
  }
  return false;
}
void Cout(long long x, const char* end = "\n") { std::cout << x << end; }
template <typename T>
void Cout(const T& x, const char* end = "\n") {
  std::cout << x << end;
}
template <typename T>
void Cout(const std::vector<T>& x, const char* sep = " ",
          const char* end = "\n") {
  for (std::size_t i = 0, sz = x.size(); i < sz; ++i) {
    std::cout << x[i] << (i == sz - 1 ? end : sep);
  }
}
struct inp {
  std::size_t szi, szj;
  inp(std::size_t _szi = 1, std::size_t _szj = 1) : szi(_szi), szj(_szj) {}
  template <typename T>
  operator T() const {
    T a;
    std::cin >> a;
    return a;
  }
  template <typename T>
  operator std::vector<T>() const {
    std::vector<T> a(szi);
    for (std::size_t i = 0; i < szi; ++i) std::cin >> a[i];
    return a;
  }
  template <typename T>
  operator std::vector<std::vector<T>>() const {
    std::vector<std::vector<T>> a(szi, std::vector<T>(szj));
    for (std::size_t i = 0; i < szi; ++i)
      for (std::size_t j = 0; j < szj; ++j) cin >> a[i][j];
    return a;
  }
};
inp inp1;
struct Edge {
  ll from;
  ll to;
  ll cost;
  ll hoge;
  Edge() = default;
  Edge(ll _to) : from(-1), to(_to), cost(1), hoge(-1){};
  Edge(ll _to, ll _cost) : from(-1), to(_to), cost(_cost), hoge(-1){};
  Edge(ll _from, ll _to, ll _cost)
      : from(_from), to(_to), cost(_cost), hoge(-1){};
  Edge(ll _from, ll _to, ll _cost, ll _hoge)
      : from(_from), to(_to), cost(_cost), hoge(_hoge){};
  bool operator<(const Edge& rhs) const { return cost < rhs.cost; }
  bool operator>(const Edge& rhs) const { return cost > rhs.cost; }
};
using Graph = vector<Edge>;
struct UnionFind {
 private:
  const int n;
  int __size;
  vector<int> uf;

 public:
  UnionFind(int _n) : n(_n), __size(_n), uf(_n, -1) {}
  int find(int x) { return (uf[x] < 0) ? x : uf[x] = find(uf[x]); }
  bool same(int x, int y) { return find(x) == find(y); }
  int size(int x) { return -uf[find(x)]; }
  int size() { return __size; }
  bool unite(int x, int y) {
    x = find(x);
    y = find(y);
    if (x == y) return false;
    __size--;
    if (uf[y] < uf[x]) swap(x, y);
    uf[x] += uf[y];
    uf[y] = x;
    return true;
  }
};
std::pair<int, int> Kruskal(Graph& G, const int v) {
  std::sort(G.begin(), G.end());
  UnionFind uni(v);
  std::pair<int, int> res;
  for (int i = 0, sz = G.size(); i < sz; ++i) {
    Edge e = G[i];
    if (uni.same(e.from, e.to)) continue;
    uni.unite(e.from, e.to);
    res.first += e.cost;
    ++res.second;
  }
  return res;
}
}  // namespace
int main() {
  int n, m;
  cin >> n >> m;
  Graph G(m * 2);
  for (ll i = 0; i < m; ++i) {
    int a, b, c;
    cin >> a >> b >> c;
    --a, --b;
    G[i * 2] = Edge(a, b, 1);
    G[i * 2 + 1] = Edge(b, a, 1);
  }
  auto need = Kruskal(G, n);
  Cout(m - need.second);
  return 0;
}
