#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#define MAXN 100100
using namespace std;
struct nod
{
int val, parent, lant, depth, lpos;
};
struct lt
{
vector<int> elem;
int parentNod;
};
nod noduri[MAXN];
lt lanturi[MAXN];
vector<int> arb[MAXN];
vector<int> arbi[MAXN];
int n, m, nq; /// nq = numarul de lanturi
void citire()
{
int x, y;
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++)
scanf("%d ", &noduri[i].val);
for (int i = 1; i <= n-1; i++) {
scanf("%d %d", &x, &y);
arb[x].push_back(y);
}
}
/// Returns weight of the subtree
int dfs (int crt, int nivel)
{
int w = 0;
noduri[crt].depth = nivel;
int maxi = -1, pos;
for (int i = 0, t = arb[crt].size(); i < t; i++)
{
int y = arb[crt][i];
if (noduri[crt].parent == y) continue;
noduri[y].parent = crt;
int nw = dfs(y, nivel+1);
if (nw > maxi) {
maxi = nw;
pos = y;
}
w += nw;
}
if (arb[crt].size()) {
for (int i = 0, t = arb[crt].size(); i < t; i++)
if (arb[crt][i] != pos)
lanturi[noduri[arb[crt][i]].lant].parentNod = crt;
lanturi[noduri[pos].lant].elem.push_back(crt);
noduri[crt].lant = noduri[pos].lant;
}
else {
lanturi[++nq].elem.push_back(crt);
noduri[crt].lant = nq;
}
return w+1;
}
void update(vector<int> v, vector<int> &arib, int crt, int st, int fn, int newValue, int pos)
{
if (st == fn) {
arib[crt] = newValue;
return;
}
int mid = (st+fn)/2;
if (pos <= mid)
update(v, arib, 2*crt, st, mid, newValue, pos);
else
update(v, arib, 2*crt+1, mid+1, fn, newValue, pos);
arib[crt] = max(arib[2*crt], arib[2*crt+1]);
}
void arbInt(vector<int> v, int ind)
{
arbi[ind].resize(4*v.size());
for (int i = 0, t = v.size(); i < t; i++) {
update(v, arbi[ind], 1, 0, v.size()-1, noduri[v[i]].val, noduri[v[i]].lpos);
}
}
int query(vector<int> v, vector<int> arib, int crt, int st, int fn, int qs, int qf)
{
if (qs <= st && qf >= fn)
return arib[crt];
int mid = (st+fn)/2;
if (qf <= mid)
return query(v, arib, 2*crt, st, mid, qs, qf);
if (qs >= mid+1)
return query(v, arib, 2*crt+1, mid+1, fn, qs, qf);
else return max(query(v, arib, 2*crt, st, mid, qs, qf),
query(v, arib, 2*crt+1, mid+1, fn, qs, qf));
}
void prepare()
{
dfs(1, 0) == n;
///Debug
// for (int i = 1; i <= nq; i++) {
// printf("\nParent is: %d\n\tVector is: ", lanturi[i].parentNod);
// for (int j = 0; j < lanturi[i].elem.size(); j++)
// printf("%d ", lanturi[i].elem[j]);
// }
for (int i = 1; i <= nq; i++)
reverse(lanturi[i].elem.begin(), lanturi[i].elem.end());
for (int i = 1; i <= nq; i++) {
for (int j = 0, t = lanturi[i].elem.size(); j < t; j++)
noduri[lanturi[i].elem[j]].lpos = j;
arbInt(lanturi[i].elem, i);
}
lanturi[noduri[1].lant].parentNod = n+5;
noduri[n+5].depth = -1;
}
int solve(int x, int y)
{
int i = x, j = y, maxim = -1;
i = x; j = y;
while (true)
{
if (noduri[i].lant == noduri[j].lant) {
int st = min(noduri[i].lpos, noduri[j].lpos);
int fn = max(noduri[i].lpos, noduri[j].lpos);
maxim = max(maxim, query(lanturi[noduri[i].lant].elem, arbi[noduri[i].lant], 1,
0, lanturi[noduri[i].lant].elem.size()-1, st, fn));
break;
}
else {
int d1 = noduri[lanturi[noduri[i].lant].parentNod].depth;
int d2 = noduri[lanturi[noduri[j].lant].parentNod].depth;
if (d1 > d2) {
maxim = max(maxim, query(lanturi[noduri[i].lant].elem, arbi[noduri[i].lant], 1,
0, lanturi[noduri[i].lant].elem.size()-1, 0, noduri[i].lpos));
i = lanturi[noduri[i].lant].parentNod;
}
else {
maxim = max(maxim, query(lanturi[noduri[j].lant].elem, arbi[noduri[j].lant], 1,
0, lanturi[noduri[j].lant].elem.size()-1, 0, noduri[j].lpos));
j = lanturi[noduri[j].lant].parentNod;
}
}
}
return maxim;
}
int main()
{
freopen("heavypath.in", "r", stdin);
freopen("heavypath.out", "w", stdout);
int t, x, y;
citire();
prepare();
for (int i = 1; i <= m; i++) {
scanf("%d %d %d", &t, &x, &y);
if (t == 0)
update(lanturi[noduri[x].lant].elem, arbi[noduri[x].lant],
1, 0, lanturi[noduri[x].lant].elem.size()-1, y, noduri[x].lpos);
else
printf("%d\n", solve(x, y));
}
return 0;
}