// 解き直し.
// https://img.atcoder.jp/nikkei2019-qual/editorial.pdf
// C++(GCC 9.2.1)
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
using P = pair<int, int>;
#define repex(i, a, b, c) for(int i = a; i < b; i += c)
#define repx(i, a, b) repex(i, a, b, 1)
#define rep(i, n) repx(i, 0, n)
#define repr(i, a, b) for(int i = a; i >= b; i--)
#define pb push_back
#define a first
#define b second
#define all(x) x.begin(), x.end()

struct edge{
    int n; // 次の頂点.
    LL w;  // 重み.
};

struct info{
    int u, v; // 頂点.
    LL w;     // 重み.
    bool operator < (const info &E) const { return w > E.w; }
};

LL X[101010];           // 各頂点の重み.
vector<edge> G[101010]; // グラフ.

// https://github.com/atcoder/live_library/blob/master/uf.cpp
// UnionFind
// coding: https://youtu.be/TdR816rqc3s?t=726
// comment: https://youtu.be/TdR816rqc3s?t=6822
// -> 一部改変.
struct UnionFind{
    vector<int> d;
    vector<LL> weights;
    UnionFind(int n = 0): d(n, -1), weights(n, 0) {}
    void setWeights(int x, LL w){
        weights[x] = w;
    }
    int find(int x){
        if(d[x] < 0) return x;
        return d[x] = find(d[x]);
    }
    bool unite(int x, int y){
        x = find(x);
        y = find(y);
        if(x == y) return false;
        if(d[x] > d[y]) swap(x, y);
        d[x] += d[y];
        d[y] = x;
        weights[x] += weights[y];
        return true;
    }
    bool same(int x, int y){
        return find(x) == find(y);
    }
    int size(int x){
        return -d[find(x)];
    }
    LL sum(int x){
        return weights[find(x)];
    }
};

int main(){
    
    // 1. 入力情報.
    int N, M;
    scanf("%d %d", &N, &M);
    rep(i, N) scanf("%lld", &X[i]);
    vector<info> v;
    rep(i, M){
        int a, b;
        LL w;
        scanf("%d %d %lld", &a, &b, &w);
        a--, b--;
        
        // 辺情報を保存.
        if(a > b) swap(a, b);
        info e;
        e.u = a;
        e.v = b;
        e.w = w;
        v.pb(e);
        
        // グラフ.
        edge e1, e2;
        e1.n = b, e2.n = a;
        e1.w = w, e2.w = w;
        G[a].pb(e1);
        G[b].pb(e2);
    }
    
    // 2. sort.
    sort(all(v)); // 降順sort.
    // puts("--- v start ---");
    // for(auto &e : v) printf("%d %d %lld\n", e.u, e.v, e.w);
    // puts("---- v end ----");
    
    // 3. 辺の重みが小さい順に, 辺を追加.
    UnionFind uf(N);
    rep(i, N) uf.setWeights(i, X[i]);
    set<P> res; // 残す辺(候補).
    repr(i, v.size() - 1, 0){
        info e = v[i];
        int u = e.u;
        int v = e.v;
        LL  w = e.w;
        if(u > v) swap(u, v);
        if(uf.find(u) != uf.find(v)){
            uf.unite(u, v);
            int r = uf.find(u);
            if(uf.sum(r) >= w) res.insert({u, v});
            // printf("1. e.u=%d e.v=%d w=%lld sum[%d]=%lld\n", u, v, e.w, r, uf.sum(r));
        }else{
            int r = uf.find(u);
            if(uf.sum(r) >= w) res.insert({u, v});
            // printf("2. e.u=%d e.v=%d w=%lld sum[%d]=%lld\n", u, v, e.w, r, uf.sum(r));
        }
    }
    // puts("--- res start ---");
    // for(auto &e : res) printf("%d %d\n", e.a, e.b);
    // puts("---- res end ----");
    
    // 4. 辺の重みが大きい順に, 残す辺をチェック.
    set<P> eRes; // 残す辺.
    set<P> memo; // 訪問済みフラグ.
    function<void(int, LL)> f = [&](int sv, LL sw) {
        
        // 1. 初期化.
        priority_queue<pair<LL, int>> pq;
        
        // 2. 始点設定.
        pq.push({sw, sv});
        
        // 3. キュー pq が 空になるまで, 探索.
        while(!pq.empty()){
            // 4. 探索を継続するために, 頂点を取得.
            LL cw = pq.top().a;
            int cv = pq.top().b;
            pq.pop();
            
            // 5. 各隣接する頂点について確認.
            for(auto &ne : G[cv]){
                LL nw = ne.w;
                int nv = ne.n;
                int a = min(cv, nv), b = max(cv, nv);
                
                // 6. 辺を保存, 訪問済みフラグ設定.
                // nw <= cw でなく nw <= sw に ロジック修正.
                // ex.
                // 辺(辺の重み)について,
                // e1(16) -> e2(1) -> e3(8) -> e4(20) の 場合に, e1, e2, e3 までを取得する必要がある.
                // 実際, 取得される辺について, 以下のように, 異なる結果となる.
                // nw <= cw の 場合: e1, e2
                // nw <= sw の 場合: e1, e2, e3
                if(!memo.count({a, b}) && nw <= sw) eRes.insert({nv, cv}), pq.push({nw, nv});
                memo.insert({a, b});
            }
        }
    };
    rep(i, v.size()){
        // 辺 e を 取得.
        info e = v[i];
        int u = e.u;
        int v = e.v;
        LL w = e.w;
        if(u > v) swap(u, v);
        
        // 今回の結果から, 残す辺に含まれるか？
        // -> 重みが, 辺 e 以下の辺も, 辿っていける場合は, すべて残す.
        if(res.count({u, v})) f(u, w);
    }
    // for(auto &p : eRes) printf("%d %d\n", p.a, p.b);
    
    // 5. 残す辺数を計算.
    int ans = M - eRes.size();
    
    // 6. 出力.
    printf("%d\n", ans);
    return 0;
    
}

Measured memory_bytes: 28392