#include <bits/stdc++.h>
#define FOR(i, a, b) for(int i=(a); i<(b); i++)
#define REP(i, n) FOR(i, 0, n)
#define all(x) (x).begin(), (x).end()
#define bit(x) (1L << (x))
using ll = long long;
using namespace std;

template<typename T>
vector<T> make_v(size_t a){return vector<T>(a);}

template<typename T,typename... Ts>
auto make_v(size_t a,Ts... ts){
    return vector<decltype(make_v<T>(ts...))>(a,make_v<T>(ts...));
}

template<typename T,typename V>
typename enable_if<is_class<T>::value==0>::type
fill_v(T &t,const V &v){t=v;}

template<typename T,typename V>
typename enable_if<is_class<T>::value!=0>::type
fill_v(T &t,const V &v){
    for(auto &e:t) fill_v(e,v);
}

class union_find{
  int n_; 
  vector<int> par, rank, size;
public:
  union_find(int n): n_{n} {
    par.resize(n_);
    rank.resize(n_);
    size.resize(n_);

    REP(i, n_){
      par[i] = i;
      rank[i] = 1;
      size[i] = 1;
    }
  } 

  bool same(int x, int y) {
    return find(x) == find(y);
  }

  void unite(int x, int y) {
    x = find(x);
    y = find(y);

    if(x == y) return;

    if(rank[x] > rank[y]){
      par[y] = x;
      size[x] += size[y];
    } else {
      par[x] = y;
      size[y] += size[x];
      if(rank[x] == rank[y]) rank[y]++;
    }
  }

  int find(int x) {
    if(par[x] == x) return x;
    else return par[x] = find(par[x]);
  }

  vector<int> get_par(){ return par; }
};

struct edge{ int u, v, w; };

vector<edge> kruskal(int n, const vector<edge>& edges) {
  vector<edge> es {edges};
  sort(es.begin(), es.end(), [](const edge& e1, const edge& e2){
    return e1.w < e2.w;
  });
  union_find uf(n);
  vector<edge> res;

  for(const auto& edge: edges){
    if(!uf.same(edge.u, edge.v)){
      uf.unite(edge.u, edge.v);
      res.emplace_back(edge);
    }
  }

  return res;
}

int main() {
  int n, m; cin >> n >> m;
  vector<edge> edges;

  REP(i, m){
    int a, b, c; cin >> a >> b >> c;
    --a; --b;
    edges.push_back({a, b, c});
  }

  cout << m - (int)kruskal(n, edges).size() << endl;

  return 0;
}
