#include <bits/stdc++.h>
#define REP(i, n) for (int i = 0; (i) < (int)(n); ++ (i))
#define REP3(i, m, n) for (int i = (m); (i) < (int)(n); ++ (i))
#define REP_R(i, n) for (int i = int(n) - 1; (i) >= 0; -- (i))
#define REP3R(i, m, n) for (int i = int(n) - 1; (i) >= (int)(m); -- (i))
#define ALL(x) begin(x), end(x)
#define dump(x) cerr << #x " = " << x << endl
#define unittest_name_helper(counter) unittest_ ## counter
#define unittest_name(counter) unittest_name_helper(counter)
#define unittest __attribute__((constructor)) void unittest_name(__COUNTER__) ()
using ll = long long;
using namespace std;
template <class T> using reversed_priority_queue = priority_queue<T, vector<T>, greater<T> >;
template <class T, class U> inline void chmax(T & a, U const & b) { a = max<T>(a, b); }
template <class T, class U> inline void chmin(T & a, U const & b) { a = min<T>(a, b); }
template <typename X, typename T> auto vectors(X x, T a) { return vector<T>(x, a); }
template <typename X, typename Y, typename Z, typename... Zs> auto vectors(X x, Y y, Z z, Zs... zs) { auto cont = vectors(y, z, zs...); return vector<decltype(cont)>(x, cont); }
template <typename T> ostream & operator << (ostream & out, vector<T> const & xs) { REP (i, int(xs.size()) - 1) out << xs[i] << ' '; if (not xs.empty()) out << xs.back(); return out; }

struct union_find_tree {
    vector<int> data;
    union_find_tree() = default;
    explicit union_find_tree(size_t n) : data(n, -1) {}
    bool is_root(int i) { return data[i] < 0; }
    int find_root(int i) { return is_root(i) ? i : (data[i] = find_root(data[i])); }
    int tree_size(int i) { return - data[find_root(i)]; }
    int unite_trees(int i, int j) {
        i = find_root(i); j = find_root(j);
        if (i != j) {
            if (tree_size(i) < tree_size(j)) swap(i,j);
            data[i] += data[j];
            data[j] = i;
        }
        return i;
    }
    bool is_same(int i, int j) { return find_root(i) == find_root(j); }
};

int solve(int n, int m, vector<ll> const & vw, vector<tuple<int, int, ll> > const & edges) {
    vector<int> order(m);
    iota(ALL(order), 0);
    sort(ALL(order), [&](int i, int j) {
        return get<2>(edges[i]) < get<2>(edges[j]);
    });

    union_find_tree uft(n);
    vector<ll> sum_vw = vw;
    vector<int> unused(n);

    for (int i : order) {
        int a, b; ll ew; tie(a, b, ew) = edges[i];
        a = uft.find_root(a);
        b = uft.find_root(b);
        if (a == b) {
            if (ew <= sum_vw[a]) {
                // nop
            } else {
                ++ unused[a];
            }
        } else {
            int c = uft.unite_trees(a, b);
            if (c != a) swap(a, b);
            assert (c == a);
            sum_vw[a] += exchange(sum_vw[b], 0ll);
            unused[a] += exchange(unused[b], 0);
            if (ew <= sum_vw[a]) {
                unused[a] = 0;
            } else {
                ++ unused[a];
            }
        }
    }

    return accumulate(ALL(unused), 0);
}

int main() {
    int n, m; cin >> n >> m;
    vector<ll> vw(n);
    REP (i, n) cin >> vw[i];
    vector<tuple<int, int, ll> > edges;
    REP (i, m) {
        int a, b; ll ew; cin >> a >> b >> ew;
        -- a; -- b;
        edges.emplace_back(a, b, ew);
    }
    cout << solve(n, m, vw, edges) << endl;
    return 0;
}


Measured memory_bytes: 4592