#include <bits/stdc++.h>
#include <sys/types.h>
#include <unistd.h>

#define _overload(_1,_2,_3,name,...) name
#define _rep(i,n) _range(i,0,n)
#define _range(i,a,b) for(int i=int(a);i<int(b);++i)
#define rep(...) _overload(__VA_ARGS__,_range,_rep,)(__VA_ARGS__)

#define _rrep(i,n) _rrange(i,n,0)
#define _rrange(i,a,b) for(int i=int(a)-1;i>=int(b);--i)
#define rrep(...) _overload(__VA_ARGS__,_rrange,_rrep,)(__VA_ARGS__)

#define _all(arg) begin(arg),end(arg)
#define uniq(arg) sort(_all(arg)),(arg).erase(unique(_all(arg)),end(arg))
#define getidx(ary,key) lower_bound(_all(ary),key)-begin(ary)
#define clr(a,b) memset((a),(b),sizeof(a))
#define bit(n) (1LL<<(n))
#define popcount(n) (__builtin_popcountll(n))

using namespace std;

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;}

using ll=long long;
using R=long double;
const R EPS=1e-9L; // [-1000,1000]->EPS=1e-8 [-10000,10000]->EPS=1e-7
inline int sgn(const R& r){return(r > EPS)-(r < -EPS);}
inline R sq(R x){return sqrt(max(x,0.0L));}

const int dx[8]={1,0,-1,0,1,-1,-1,1};
const int dy[8]={0,1,0,-1,1,1,-1,-1};

const pid_t pid = getpid();
// Problem Specific Parameter:

const int limit = 100010;
int A[limit],B[limit],X[limit],Y[limit];


int sz = 1;
set<int> tree[limit];
int idx[limit];
ll total[limit];


vector<int> vs[2];
bool visited[limit];


void cut(int n,int a,int b){
	if(tree[a].count(b) == 0) return;
	tree[a].erase(b);
	tree[b].erase(a);

	const int basis = idx[a];
	rep(i,2) vs[i].clear();

	queue<int> q[2];
	q[0].push(a);
	q[1].push(b);

	while(!q[0].empty() and !q[1].empty()){
		rep(i,2){
			if(!q[i].empty()){
				int v = q[i].front(); q[i].pop();
				visited[v] = true;
				vs[i].push_back(v);
				for(auto &v2:tree[v]){
					if(visited[v2]) continue;
					q[i].push(v2);
				}
			}
		}
	}

	const int pos = (vs[0].size() + q[0].size()) > (vs[1].size() + q[1].size());

	for(auto &v: vs[pos]){
		idx[v] = sz;
		total[basis] -= X[v];
		total[sz] += X[v];
	}

	rep(i,2){
		for(auto &v:vs[i]){
			visited[v] = false;
		}
	}

	sz++;
}

// Description: 素集合を管理するデータ構造
// TimeComplexity: 初期化$\mathcal{O}(n)$ 更新$\mathcal{O}(\log n)$
// Verifyed: AOJ DSL_1_A

struct Union_find {
	Union_find(int n) {data.assign(n, -1);}
	int find(int x) {return (data[x] < 0) ? x : data[x] = find(data[x]);}
	void unite(int a, int b) {
		a = find(a), b = find(b);
		if (a != b) {
			if (data[a] < data[b]) swap(a, b);
			data[a] += data[b], data[b] = a;
		}
	}
	bool same(int a, int b) {return find(a) == find(b);}
	int size(int x) {return -data[x];}
	vector<int> data;
};

int main(void){
	int n,m;
	cin >> n >> m;
	rep(i,n) cin >> X[i];

	using edge = tuple<ll,int,int>;
	vector<edge> graph;

	rep(i,m){
		cin >> A[i] >> B[i] >> Y[i];
		A[i]--,B[i]--;
		graph.push_back(edge(Y[i],A[i],B[i]));
	}

	Union_find uf(n);
	sort(begin(graph),end(graph));

	for(auto &e:graph){
		int y,a,b;
		tie(y,a,b) = e;
		if(uf.same(a,b)) continue;
		tree[a].insert(b);
		tree[b].insert(a);
		uf.unite(a,b);
	}

	reverse(begin(graph),end(graph));

	rep(i,n) total[0] += X[i];

	int ans = 0;
	for(auto &e:graph){
		int y,a,b;
		tie(y,a,b) = e;
		const ll cur = total[idx[a]];
		if(cur >= y) continue;
		ans++;
		cut(n,a,b);
	}

	cout << ans << endl;
	
	return 0;
}

Measured memory_bytes: 19440