#include <bits/stdc++.h>
using namespace std;
ifstream f ("heavypath.in");
ofstream g ("heavypath.out");
const int Dim = 1e5 + 5;
int w[ Dim ], v[ Dim ], nivel[ Dim ], aint[5 * Dim], l[ Dim ], DimLant[ Dim ], NivLant[ Dim ], TataLant[ Dim ], Dec[ Dim ];
int n, m, type, x, y, NrLanturi;
vector <int> P[ Dim ], G[ Dim ];
void build_tree (int inc, int sf, int node, int dec, int lant) {
if (inc == sf) {
aint[node + dec] = v[P[ lant ][inc - 1]];
return;
}
int mid = (inc + sf) / 2;
build_tree (inc, mid, 2 * node, dec, lant);
build_tree (mid + 1, sf, 2 * node + 1, dec, lant);
aint[node + dec] = max (aint[2 * node + dec], aint[2 * node + 1 + dec]);
}
void update_tree (int inc, int sf, int pos, int val, int node, int dec) {
if (inc == sf) {
aint[node + dec] = val;
return;
}
int mid = (inc + sf) / 2;
if (pos <= mid)
update_tree (inc, mid, pos, val, 2 * node, dec);
else
update_tree (mid + 1, sf, pos, val, 2 * node + 1, dec);
aint[node + dec] = max (aint[2 * node + dec], aint[2 * node + 1 + dec]);
}
int query_tree (int inc, int sf, int a, int b, int node, int dec) {
if (inc > sf || a > sf || b < inc) {
return 0;
}
if (a <= inc && sf <= b)
return aint[node + dec];
int mid = (inc + sf) / 2;
if (b <= mid) {
return query_tree (inc, mid, a, b, 2 * node, dec);
}
if (a > mid) {
return query_tree (mid + 1, sf, a, b, 2 * node + 1, dec);
}
int q1 = query_tree (inc, mid, a, b, 2 * node, dec);
int q2 = query_tree (mid + 1, sf, a, b, 2 * node + 1, dec);
return max (q1, q2);
}
void dfs (int node, int tata) {
nivel[ node ] = nivel[ tata ] + 1;
w[ node ] = 1;
int pos, gr = -1;
bool frunza = true;
for (vector <int> :: iterator it = G[ node ].begin(); it != G[ node ].end(); ++it) {
if (*it != tata) {
frunza = false;
dfs (*it, node);
w[ node ] += w[ *it ];
if (w[ *it ] > gr) {
gr = w[ *it ];
pos = *it;
}
}
}
if (frunza) {
l[ node ] = ++NrLanturi;
DimLant[ NrLanturi ] = 1;
P[ NrLanturi ].push_back ( node );
}
else {
l[ node ] = l[ pos ];
++DimLant[l[ node ]];
P[l[ node ]].push_back ( node );
for (vector <int> :: iterator it = G[ node ].begin(); it != G[ node ].end(); ++it) {
if (*it != tata && l[ *it ] != l[ node ]) {
TataLant[l[ *it ]] = node;
NivLant[l[ *it ]] = nivel[ node ];
}
}
}
}
void make_paths () {
dfs (1, 0);
for (int i = 1; i <= NrLanturi; ++i) {
reverse (P[ i ].begin(), P[ i ].end());
if (i > 1) {
Dec[ i ] = Dec[i - 1] + 4 * DimLant[i - 1];
}
build_tree (1, DimLant[ i ], 1, Dec[ i ], i);
}
}
void update (int x, int y) {
update_tree (1, DimLant[l[ x ]], nivel[ x ] - NivLant[l[ x ]], y, 1, Dec[l[ x ]]);
}
void solve (int x, int y) {
int ans = 0;
while (1) {
if (l[ x ] == l[ y ]) {
if (nivel[ x ] > nivel[ y ])
swap (x, y);
ans = max (ans, query_tree (1, DimLant[l[ x ]], nivel[ x ] - NivLant[l[ x ]], nivel[ y ] - NivLant[l[ y ]], 1, Dec[l[ x ]]));
g << ans << '\n';
return;
}
if (NivLant[l[ x ]] < NivLant[l[ y ]])
swap (x, y);
ans = max (ans, query_tree (1, DimLant[l[ x ]], 1, nivel[ x ] - NivLant[l[ x ]], 1, Dec[l[ x ]]));
x = TataLant[l[ x ]];
}
}
int main() {
f >> n >> m;
for (int i = 1; i <= n; ++i) {
f >> v[ i ];
}
for (int i = 1; i < n; ++i) {
f >> x >> y;
G[ x ].push_back ( y );
G[ y ].push_back ( x );
}
make_paths ();
for (int i = 1; i <= m; ++i) {
f >> type >> x >> y;
if (type == 0) {
update (x, y);
}
else {
solve (x, y);
}
}
return 0;
}