#include <vector>
#pragma GCC target ("avx2")
#pragma GCC optimize ("O3")
#pragma GCC optimize ("unroll-loops")
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;
using namespace __gnu_pbds;
#define A(a) begin(a),end(a)
#define pb push_back
#define pp partition_point
#define K first
#define V second
#define krep(i,a,b) for (int i = a; i < (b); ++i)
#define all(x) begin(x), end(x)
#define sz(x) (int) (size(x))
template <typename T>
std::istream& operator >>(std::istream& input, std::pair <T, T> & data)
{
input >> data.first >> data.second;
return input;
}
template <typename T>
std::istream& operator >>(std::istream& input, std::vector<T>& data)
{
for (T& x : data)
input >> x;
return input;
}
template <typename T>
std::ostream& operator <<(std::ostream& output, const pair <T, T> & data)
{
output << "(" << data.first << "," << data.second << ")";
return output;
}
template <typename T>
std::ostream& operator <<(std::ostream& output, const std::vector<T>& data)
{
for (const T& x : data)
output << x << " ";
return output;
}
struct chash {
static uint64_t splitmix64(uint64_t x) {
// http://xorshift.di.unimi.it/splitmix64.c
x += 0x9e3779b97f4a7c15;
x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
return x ^ (x >> 31);
}
size_t operator()(uint64_t x) const {
static const uint64_t FIXED_RANDOM = chrono::steady_clock::now().time_since_epoch().count();
return splitmix64(x + FIXED_RANDOM);
}
};
template<typename T, typename U> using pb_map = gp_hash_table<T, U, chash>;
template<typename T> using pb_set = gp_hash_table<T, null_type, chash>;
using ll = long long;
using ld = long double;
using vi = vector<int>;
using vii = vector<vector<int>>;
using vll = vector<ll>;
using pll = pair<ll,ll>;
using pii = pair<int,int>;
template<class T> bool ckmin(T& a, const T& b) { return b < a ? (a = b, 1) : 0; }
template<class T> bool ckmax(T& a, const T& b) { return a < b ? (a = b, 1) : 0; }
inline long long Bit(long long mask, long long bit) { return (mask >> bit) & 1; }
constexpr ll NN = 1e6, M = 1000000007, L = 20;
const ll oo = 1e16;
/*
i need multisets with lazy add
*/
struct NickSet{
ll tag = 0;
set<ll> st;
void insert(ll x){
st.insert(x - tag);
}
ll top(){
return *st.rbegin() + tag;
}
};
void run(){
ll n; cin >> n;
vll w(n); cin >> w;
vector<vector<array<ll,2>>> adj(n);
for(int z =0;z<n-1;z++){
ll u,v,w;
cin >> u >> v >> w;
--u,--v;
adj[u].pb({v,w});
adj[v].pb({u,w});
}
vll v(n,oo);
{
function<void(int,int)> dfs = [&](int i,int p){
for(auto [j,w] : adj[i]) if(j!=p) v[j]=w,dfs(j,i);
}; dfs(0,-1);
}
ll ans = -oo;
vector<NickSet> dp(n);
function<void(int,int)> dfs = [&](int i,int p){
vll bc;
//now i one consider UP paths, here i cannot consider my immediate children
for(auto [j,_] : adj[i]) if(j!=p){
dfs(j,i);
if(not dp[j].st.empty()){
ckmax(ans,dp[j].top() + w[i]);//this gets my up paths but not length 1 ones
}
dp[j].insert(w[j] - 2*v[j]);
bc.pb(dp[j].top());
}
sort(A(bc)), reverse(A(bc));
if((int)bc.size()>=2){//here i do assume my children are already inside it
ckmax(ans,bc[0]+bc[1]+w[i]);
}
for(auto [j,_] : adj[i]) if(j!=p){
if(dp[i].st.size() < dp[j].st.size()) swap(dp[i],dp[j]);
for(ll x : dp[j].st) dp[i].insert(x+dp[j].tag);
}
dp[i].tag -= 2*v[i];
dp[i].tag += w[i];
}; dfs(0,-1);
cout << ans << '\n';
}
int main()
{
//KING OF THE WORLD...... U.W.T.B
//nick belov always reads the entire problem statement.
cin.tie(0);
ios_base::sync_with_stdio(false);
int t; cin>>t; while(t--) run();
}