#include <bits/stdc++.h>
using namespace std;
struct edge {
  int a, b, y;
  bool operator<(const struct edge& rhs) const { return y < rhs.y; }
};
struct union_find {
  vector<long long> data;
  union_find(vector<int>& X) : data(X.size()) {
    for (int i = 0; i < X.size(); i++) data[i] = -X[i];
  }
  void unite(int x, int y) {
    if (root(x) == root(y)) return;
    if (size(x) < size(y)) swap(x, y);
    data[root(x)] += data[root(y)];
    data[y] = root(x);
  }
  int root(int x) {
    if (data[x] < 0) return x;
    return data[x] = root(data[x]);
  }
  long long size(int x) { return -data[root(x)]; }
};
int main() {
  int N, M;
  cin >> N >> M;
  vector<int> X(N);
  for (int& x : X) cin >> x;
  vector<vector<pair<int, int>>> G(N);
  vector<edge> E(M);
  for (auto& e : E) {
    cin >> e.a >> e.b >> e.y, e.a--, e.b--;
    G[e.a].emplace_back(e.b, e.y);
    G[e.b].emplace_back(e.a, e.y);
  }
  sort(begin(E), end(E));
  union_find uf(X);
  vector<int> candidate(M);
  for (int i = 0; i < E.size(); i++) {
    uf.unite(E[i].a, E[i].b);
    if (uf.size(E[i].a) >= E[i].y) {
      candidate[i] = 1;
    }
  }
  vector<int> visited(N);
  int cnt = 0;
  function<void(int, int)> recur;
  recur = [&](int x, int cost) {
    if (visited[x]) return;
    visited[x] = 1;
    for (auto p : G[x]) {
      if (p.second <= cost) {
        cnt++;
        recur(p.first, cost);
      }
    }
  };
  for (int i = E.size() - 1; i >= 0; i--) {
    if (!visited[i] && candidate[i]) {
      recur(E[i].a, E[i].y);
    }
  }
  cout << M - cnt / 2 << endl;
  return 0;
}
