#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2000005;
int n, m, a[N], tot, cnt, head[N];
struct Node{
int Lmax, Rmax, Smax, sum, add;
bool flag;
Node() {Lmax = Rmax = Smax = sum = 0;}
}Tree[N];
struct jc{
int size, fa, d, hson, seg, rev, top;
}E[N];
struct T{
int to, net;
}e[N];
void add(int x, int y) {
e[++tot].to = y;
e[tot].net = head[x];
head[x] = tot;
}
void dfs1(int x, int F) {
E[x].fa = F, E[x].size = 1, E[x].d = E[F].d + 1;
for (int i = head[x]; i; i = e[i].net) {
int y = e[i].to;
if (y == F) continue;
dfs1(y, x);
E[x].size += E[y].size;
if (E[y].size > E[E[x].hson].size) E[x].hson = y;
}
}
void dfs2(int x, int Top) {
E[x].top = Top, E[x].seg = ++cnt, E[cnt].rev = x;
if (E[x].hson) dfs2(E[x].hson, Top);
for (int i = head[x]; i; i = e[i].net) {
int y = e[i].to;
if (!E[y].top) dfs2(y, y);
}
}
Node Push_up (Node l, Node r) {
Node ans;
ans.sum = l.sum + r.sum;
ans.Lmax = max(l.Lmax, l.sum + r.Lmax);
ans.Rmax = max(r.Rmax, r.sum + l.Rmax);
ans.Smax = max(l.Rmax + r.Lmax, max(l.Smax, r.Smax));
ans.add = ans.flag = 0;
return ans;
}
void Build (int p, int l, int r) {
if(l == r) {
Tree[p].sum = a[E[l].rev];
Tree[p].Lmax = Tree[p].Rmax = Tree[p].Smax = max(Tree[p].sum, (int)0);
Tree[p].flag = 0;
return ;
}
int mid = (l + r) >> 1;
Build(p * 2, l, mid);
Build(p * 2 + 1, mid + 1, r);
Tree[p] = Push_up(Tree[p * 2], Tree[p * 2 + 1]);
}
void Push_down(int p, int l, int r) {
if(!Tree[p].add) return ;
int mid = (l + r) / 2;
Tree[p * 2].add = Tree[p].add;
Tree[p * 2 + 1].add = Tree[p].add;
Tree[p * 2].sum = Tree[p].add * (mid - l + 1);
Tree[p * 2 + 1].sum = Tree[p].add * (r - mid);
Tree[p * 2].flag = Tree[p * 2 + 1].flag = 1;
Tree[p * 2].Lmax = Tree[p * 2].Rmax = Tree[p * 2].Smax = max (Tree[p * 2].sum, (int)0);
Tree[p * 2 + 1].Lmax = Tree[p * 2 + 1].Rmax = Tree[p * 2 + 1].Smax = max (Tree[p * 2 + 1].sum, (int)0);
Tree[p].add = Tree[p].flag = 0;
}
void Change(int p, int l, int r, int L, int R, int k) {
if (l > R || r < L) return ;
if (l >= L && r <= R) {
Tree[p].sum = k * (r - l + 1);
Tree[p].Lmax = Tree[p].Rmax = Tree[p].Smax = max(Tree[p].sum, (int)0);
Tree[p].flag = 1;
Tree[p].add = k;
return ;
}
Push_down(p, l, r);
int mid = (l + r) / 2;
if (L <= mid) Change(p * 2, l, mid, L, R, k);
if (R > mid) Change(p * 2 + 1, mid + 1, r, L, R, k);
Tree[p] = Push_up(Tree[p * 2], Tree[p * 2 + 1]);
}
Node Query(int p, int l, int r, int L, int R) {
if(l <= L && R <= r) return Tree[p];
Push_down(p, l, r);
int mid = (l + r) / 2;
Node ll, rr;
if(L <= mid) ll = Query(p * 2, l, mid, L, R);
if(R > mid) rr = Query(p * 2 + 1, mid + 1, r, L, R);
return Push_up(ll, rr);
}
void iChange(int x, int y, int val) {
int topx = E[x].top, topy = E[y].top;
while(topx != topy) {
if (E[topx].d < E[topy].d) {
swap(x, y);
swap(topx, topy);
}
Change(1, 1, cnt, E[topx].seg, E[x].seg, val);
x = E[topx].fa;
topx = E[x].top;
}
if (E[x].d > E[y].d) swap(x, y);
Change(1, 1, cnt, E[x].seg, E[y].seg, val);
}
Node iQuery(int x, int y) {
int topx = E[x].top, topy = E[y].top;
Node L, R;
while(topx != topy) {
if (E[topx].d < E[topy].d) {
R = Push_up(Query(1, 1, cnt, E[topx].seg, E[y].seg), R);
y = E[topy].fa;
topy = E[y].top;
}
else {
L = Push_up(Query(1, 1, cnt, E[topx].seg, E[x].seg), L);
x = E[topx].fa;
topx = E[x].top;
}
}
if (E[x].d > E[y].d) L = Push_up(Query(1, 1, cnt, E[y].seg, E[x].seg), L);
else R = Push_up(Query(1, 1, cnt, E[x].seg, E[y].seg), R);
swap(L.Lmax, R.Rmax);
return Push_up(L, R);
}
signed main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i < n; i++) {
int x, y;
cin >> x >> y;
add(x, y);
add(y, x);
}
dfs1(1, 0);
dfs2(1, 1);
Build(1, 1, cnt);
cin >> m;
while (m--) {
int op, x, y, z;
cin >> op >> x >> y;
if (op == 1) {
cout << iQuery(x, y).Smax << '\n';
}
else {
cin >> z;
iChange(x, y, z);
}
}
return 0;
}