#include "bits/stdc++.h"
#define _overload3(_1,_2,_3,name,...) name
#define _rep(i,n) repi(i,0,n)
#define repi(i,a,b) for(int i=int(a),i##_len=(b);i<i##_len;++i)
#define MSVC_UNKO(x) x
#define rep(...) MSVC_UNKO(_overload3(__VA_ARGS__,repi,_rep,_rep)(__VA_ARGS__))
#define all(c) c.begin(),c.end()
#define mp make_pair
#define write(x) cout<<(x)<<"\n"
using namespace std; typedef long long ll; typedef vector<int> vi; typedef vector<ll> vll;
template<class T>using vv = vector<vector<T>>; template<class T>auto vvec(int n, int m, T v) { return vv<T>(n, vector<T>(m, v)); }
template<class T>bool chmax(T& a, const T& b) { return a < b ? a = b, 1 : 0; }
template<class T>bool chmin(T& a, const T& b) { return b < a ? a = b, 1 : 0; }
constexpr int INF = 1 << 29, MOD = int(1e9) + 7; constexpr ll LINF = 1LL << 60;
struct aaa { aaa() { cin.tie(0); ios::sync_with_stdio(0); cout << fixed << setprecision(10); }; }aaaa;

    struct UnionFindTree {
        vector<ll> parent, rank;
        int operator [](int x) { return find(x); }
        void init(int n) { parent.assign(n, -1); rank.assign(n, 0); }
        int find(int x) { return parent[x] < 0 ? x : (parent[x] = find(parent[x])); }
        bool same(int x, int y) { return find(x) == find(y); }
        ll size(int x) { return -parent[find(x)]; }
        bool unite(int x, int y) {
            if ((x = find(x)) == (y = find(y))) return 0;
            rank[x] = max(rank[x], rank[y] + 1); parent[x] += parent[y]; parent[y] = x; return 1;
        }
        bool uniteByRank(int x, int y) {
            if ((x = find(x)) == (y = find(y))) return 0; else if (rank[x] < rank[y]) swap(x, y);
            else if (rank[x] == rank[y]) rank[x]++; parent[x] += parent[y]; parent[y] = x; return 1;
        }
        bool uniteBySize(int x, int y) {
            if ((x = find(x)) == (y = find(y))) return 0; else if (size(x) < size(y)) swap(x, y);
            rank[x] = max(rank[x], rank[y] + 1); parent[x] += parent[y]; parent[y] = x; return 1;
        }
    };

int dfs(vector<vector<pair<int, int>>>& graph, vector<bool>& visited, int v, int weightLimit) {
    visited[v] = true;
    int n_edge = 0;
    for (auto& u : graph[v]) {
        if (u.first > weightLimit) continue;
        n_edge++;
        if (!visited[u.second]) {
            n_edge +=dfs(graph, visited, u.second, weightLimit);
        }
    }
    return n_edge;
}

int main() {
    int N, M;
    cin >> N >> M;

    UnionFindTree uf;
    uf.init(N);
    rep(i, N) {
        int x;
        cin >> x;
        uf.parent[i] = -x;
    }

    vector<pair<int, pair<int, int>>> edge(M);
    vector<vector<pair<int, int>>> graph(N);
    rep(i, M) {
        int a, b, y;
        cin >> a >> b >> y;
        a--; b--;
        edge[i] = mp(y, mp(a, b));
        graph[a].emplace_back(y, b);
        graph[b].emplace_back(y, a);
    }

    sort(all(edge));
    vector<bool> canbeLargest(M);

    for (int i = 0; i < M; ) {
        int j = i + 1;
        for (; j < M; ++j) {
            if (edge[j].first > edge[i].first) break;
        }

        for (int k = i; k < j; ++k) {
            uf.uniteByRank(edge[k].second.first, edge[k].second.second);
        }

        for (int k = i; k < j; ++k) {
            if (uf.size(edge[k].second.first) >= edge[k].first) {
                canbeLargest[k] = true;
            }
        }

        i = j;
    }

    vector<bool> visited(N);
    int ans = M;
    for (int i = M - 1; i >= 0; --i) {
        if (canbeLargest[i] && !visited[edge[i].second.first]) {
            ans -= dfs(graph, visited, edge[i].second.first, edge[i].first) / 2;
        }
    }

    write(ans);
}

Measured memory_bytes: 16256