#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
using ld = long double;
class UnionFind {
 public:
  explicit UnionFind(ll size)
      : mParent(size, -1), mRank(size, 0), mData(size, 0) {}
  bool unionSet(ll x, ll y) {
    if ((x = root(x)) == (y = root(y))) return false;
    if (mRank[x] < mRank[y]) {
      mParent[y] += mParent[x];
      mParent[x] = y;
      unionData(mData[y], mData[x]);
    } else {
      mParent[x] += mParent[y];
      mParent[y] = x;
      if (mRank[x] == mRank[y]) ++mRank[x];
      unionData(mData[x], mData[y]);
    }
    return true;
  }
  bool isEqualSet(ll x, ll y) { return root(x) == root(y); }
  ll root(ll x) { return mParent[x] < 0 ? x : mParent[x] = root(mParent[x]); }
  ll size(ll x) { return -mParent[root(x)]; }
  void unionData(ll& parent, ll& child) { parent += child; }
  void setData(ll x, ll data) { mData[root(x)] = data; }
  ll getData(ll x) { return mData[root(x)]; }

 public:
  vector<ll> mParent;
  vector<ll> mRank;
  vector<ll> mData;
};
struct St {
  ll a = 0;
  ll b = 0;
  ll y = 0;
  bool is = false;
  bool iss = false;
};
bool operator<(const St& l, const St& r) {
  if (l.y == r.y) {
    if (l.a == r.a)
      return l.b < r.b;
    else
      return l.a < r.a;
  }
  return l.y < r.y;
}
bool operator<=(const St& l, const St& r) {
  if (l.y == r.y) return true;
  return l < r;
}
bool operator>(const St& l, const St& r) {
  if (l.y == r.y) {
    if (l.a == r.a)
      return l.b > r.b;
    else
      return l.a > r.a;
  }
  return l.y > r.y;
}
int main() {
  ll n, m;
  cin >> n >> m;
  vector<ll> x(n, 0);
  for (int i = 0; i < (n); ++i) cin >> x[i];
  vector<St> edgeSt(m, St());
  for (int i = 0; i < (m); ++i) {
    ll a, b, y;
    cin >> a >> b >> y;
    edgeSt[i].a = a - 1;
    edgeSt[i].b = b - 1;
    edgeSt[i].y = y;
  }
  sort(edgeSt.begin(), edgeSt.end(), less<St>());
  UnionFind uf(n);
  for (int i = 0; i < (n); ++i) uf.setData(i, x[i]);
  for (int i = 0; i < (m); ++i) {
    uf.unionSet(edgeSt[i].a, edgeSt[i].b);
    auto d = uf.getData(edgeSt[i].a);
    if (d >= edgeSt[i].y) edgeSt[i].is = true;
  }
  vector<vector<ll>> edge(n);
  for (int i = 0; i < (m); ++i) {
    edge[edgeSt[i].a].push_back(i);
    edge[edgeSt[i].b].push_back(i);
  }
  ll ans = 0;
  for (int i = (m)-1; i >= 0; --i) {
    if (edgeSt[i].iss) {
      continue;
    }
    if (!edgeSt[i].is) {
      ++ans;
      continue;
    }
    stack<ll> st;
    vector<bool> used(n, false);
    st.push(edgeSt[i].a);
    used[edgeSt[i].a] = true;
    st.push(edgeSt[i].b);
    used[edgeSt[i].b] = true;
    while (st.size()) {
      ll t = st.top();
      st.pop();
      for (auto idx : edge[t]) {
        auto& e = edgeSt[idx];
        auto eb = e.b != t ? e.b : e.a;
        if (!used[eb] && e <= edgeSt[i]) {
          used[eb] = true;
          e.iss = true;
          st.push(eb);
        }
      }
    }
  }
  cout << ans << endl;
  return 0;
}
