#include<iostream>
#include<fstream>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<queue>
#include<map>
#include<set>
#include<algorithm>
#include<iomanip>
#include<bitset>
using namespace std;
const int N = 101000;
const int inf = 2000000000;
struct aint {
vector<int> smax;
int n;
aint(int nn) {
n = nn;
smax.resize(4 * n + 6);
}
void _update(int nod, int pozx, int pozy, int poz1, int poz2, int val) {
if(pozx >= poz1 && poz2 >= pozy) {
smax[nod] = val;
return;
}
int mid = (pozx + pozy) / 2;
if(mid >= poz1)
_update(2 * nod, pozx, mid, poz1, poz2, val);
if(mid < poz2)
_update(2 * nod + 1, mid + 1, pozy, poz1, poz2, val);
smax[nod] = max(smax[2 * nod], smax[2 * nod + 1]);
}
void update(int pozx, int pozy, int val) {_update(1, 1, n, pozx, pozy, val);}
int _query(int nod, int pozx, int pozy, int poz1, int poz2) {
if(pozx >= poz1 && poz2 >= pozy)
return smax[nod];
int r = -inf, mid = (pozx + pozy) / 2;
if(mid >= poz1)
r = _query(2 * nod, pozx, mid, poz1, poz2);
if(mid < poz2)
r = max(r, _query(2 * nod + 1, mid + 1, pozy, poz1, poz2));
return r;
}
int query(int pozx, int pozy) {
if(pozx > pozy)
swap(pozx, pozy);
if(!pozy)
return 0;
return _query(1, 1, n, pozx, pozy);
}
};
int n, m;
vector<int> v[N];
int nrel[N], cs[N], cd[N], te, niv[N];
int val[N];
void calc1(int nod) {
nrel[nod] = 1;
cs[nod] = ++te;
for(vector<int>::iterator it = v[nod].begin(); it != v[nod].end(); ++it) if(!nrel[*it]) {
niv[*it] = niv[nod] + 1;
calc1(*it);
nrel[nod] += nrel[*it];
}
cd[nod] = te;
}
int lantc, sizeLant[N], nl[N], pozNod[N], lastNod[N];
vector<aint> arb;
void calch(int nod, int par, int la, int pozz) {
int smax = 0, elmax;
vector<int>::iterator it;
pozNod[nod] = pozz;
sizeLant[la]++;
nl[nod] = la;
for(it = v[nod].begin(); it != v[nod].end(); ++it) if(*it != par)
if(nrel[*it] > smax) {
smax = nrel[*it];
elmax = *it;
}
for(it = v[nod].begin(); it != v[nod].end(); ++it) if(*it != par) {
if(*it == elmax) {
calch(*it, nod, la, pozz + 1);
}
else {
++lantc;
lastNod[lantc] = nod;
calch(*it, nod, lantc, 1);
}
}
}
void update(int nod, int val) {
arb[nl[nod]].update(pozNod[nod], pozNod[nod], val);
}
int query(int nod1, int nod2) {
int rez = -inf;
while(nl[nod1] != nl[nod2]) {
if(niv[lastNod[ nl[nod1] ]] > niv[lastNod[ nl[nod2] ]])
swap(nod1, nod2);
int t = arb[nl[nod2]].query(1, pozNod[nod2]);
rez = max(rez, t);
nod2 = lastNod[nl[nod2]];
}
rez = max(rez, arb[nl[nod2]].query(pozNod[nod1], pozNod[nod2]));
return rez;
}
int main() {
int i;
freopen("heavypath.in", "r", stdin);
freopen("heavypath.out", "w", stdout);
cin >> n >> m;
for(i = 1; i <= n; ++i)
cin >> val[i];
for(i = 1; i < n; ++i) {
int a, b;
scanf("%d%d", &a, &b);
v[a].push_back(b);
v[b].push_back(a);
}
calc1(1);
niv[0] = -1;
lantc = 0;
calch(1, 0, 0, 1);
for(i = 0; i <= lantc; ++i) {
arb.push_back(*(new aint(sizeLant[i])));
}
for(i = 1; i <= n; ++i)
update(i, val[i]);
for(i = 1; i <= m; ++i) {
int a;
int c, d;
scanf("%d", &a);
scanf("%d%d", &c, &d);
if(a == 0) {
update(c, d);
}
else {
cout << query(c, d) << "\n";
}
}
return 0;
}