#include <bits/stdc++.h>
using namespace std;
using uint = unsigned int;
using lint = long long int;
using ulint = unsigned long long int;
template<class T = int> using V = vector<T>;
template<class T = int> using VV = V< V<T> >;
template<class T, class U> void assign(V<T>& v, int n, const U& a) { v.assign(n, a); }
template<class T, class... Args> void assign(V<T>& v, int n, const Args&... args) { v.resize(n); for (auto&& e : v) assign(e, args...); }


// uniteが呼ばれる度に時刻tが1進む
struct PPUnionFind {
  const int n;

  PPUnionFind(const V<>& x) : n(x.size()), curr(0), time(n, V<>(1)), dat(n) {
    for (int v = 0; v < n; ++v) {
      dat[v] = {-x[v]};
    }
  }

  bool same(int u, int v, int t) const {
    assert(0 <= u and u < n);
    assert(0 <= v and v < n);
    assert(0 <= t and t <= curr);
    return find(u, t) == find(v, t);
  }
  bool same(int u, int v) const {
    return same(u, v, curr);
  }

  lint size(int v, int t) const {
    assert(0 <= v and v < n);
    assert(0 <= t and t <= curr);
    v = find(v, t);
    return -dat[v][distance(begin(time[v]), --upper_bound(begin(time[v]), end(time[v]), t))];
  }
  lint size(int v) const {
    return size(v, curr);
  }

  void unite(int u, int v) {
    assert(0 <= u and u < n);
    assert(0 <= v and v < n);
    ++curr;
    if ((u = find(u, curr)) == (v = find(v, curr))) return;
    time[u].push_back(curr);
    time[v].push_back(curr);
    if (-dat[u].back() < -dat[v].back()) {
      dat[v].push_back(dat[v].back() + dat[u].back());
      dat[u].push_back(v);
    } else {
      dat[u].push_back(dat[u].back() + dat[v].back());
      dat[v].push_back(u);
    }
  }

private:
  int curr;
  VV<> time;
  VV<lint> dat;

  int find(int v, int t) const {
    return dat[v].back() < 0 or time[v].back() > t ? v : find(dat[v].back(), t);
  }
};

int main() {
  cin.tie(nullptr); ios_base::sync_with_stdio(false);
  int n, m; cin >> n >> m;
  V<> x(n); for (int i = 0; i < n; ++i) cin >> x[i];
  struct Edge {
    int u, v, y;
    bool operator<(const Edge& rhs) { return y < rhs.y; }
  };
  V<Edge> es(m);
  for (int i = 0; i < m; ++i) {
    int u, v, y; cin >> u >> v >> y, --u, --v;
    es[i] = {u, v, y};
  }
  sort(begin(es), end(es));
  PPUnionFind uf(x);
  for (const auto& e : es) {
    uf.unite(e.u, e.v);
  }
  int res = 0;
  int t = m;
  V< set<int> > g(n);
  for (const auto& e : es) {
    g[e.u].insert(e.v);
    g[e.v].insert(e.u);
  }
  V<bool> ok(n);
  auto dfs = [&](const auto& dfs, int v) -> void {
    ok[v] = true;
    for (int w : g[v]) if (!ok[v]) {
      dfs(dfs, w);
    }
  };
  for (int i = m - 1; i >= 0; --i) {
    if (ok[es[i].u]) continue;
    if (es[i].y > uf.size(es[i].u, t)) {
      ++res;
      t = i;
      g[es[i].u].erase(es[i].v);
      g[es[i].v].erase(es[i].u);
    } else {
      dfs(dfs, es[i].u);
    }
  }
  cout << res << '\n';
}