#include <fstream>
#include <algorithm>
#include <vector>
using namespace std;
const int NMAX = 100002;
const int LOG = 18;
ifstream cin("heavypath.in");
ofstream cout("heavypath.out");
vector <vector <int>> adj;
struct noduri {
int val; ///val init pe care o are nodul, pt queryuri
int tata;
int depth;
int weight; ///cate lanturi (inclusiv el)sunt sub el
int lant; ///in ce lant apartine
int pos; ///pozitia in vectorul sortat
}v[NMAX];
vector <int> l;///primul, cel mai de sus din lant
int topo[NMAX]; ///ord nodurilor dupa lanturi si depth
int n;
void dfs(int start) {
bool ok = 0;
for(auto nod : adj[start]) {
if(nod == v[start].tata)
continue;
v[nod].tata = start;
v[nod].depth = v[start].depth + 1;
dfs(nod);
ok = 1;
}
if(!ok) { ///frunza, init un lant
l.push_back(start);
v[start].lant = l.size() - 1;
v[start].weight = 1; ///ca are doar un lant
return;
}
int maxx = 0, cnt = 0;
for(auto nod : adj[start]) { ///gasim copilul cu nr de lanturi maxim
if(nod == v[start].tata)
continue;
if(v[nod].weight > maxx) {
maxx = v[nod].weight;
cnt = 1;
}
else if(v[nod].weight == maxx)
cnt++;
}
///noi ne unim cu copilul respectiv, dar oricum cautam
///nrmax de lanturi de sub el (de pe o ramura), deci:
///- dc sunt mai multe maxuri, o sa fie maxx + 1
///- dc e doar un max, adunci e max(maxx, urm cel mai mare + 1),
///deci tot max
if(cnt > 1)
v[start].weight = maxx + 1;
else
v[start].weight = maxx;
for(auto nod : adj[start]) { ///ne unim cu un lant
if(nod == v[start].tata)
continue;
if(v[nod].weight == maxx) {
v[start].lant = v[nod].lant;
l[v[start].lant] = start; ///updatam si in lant
break;
}
}
}
bool cmp(int a, int b) { ///pt sortarea topo
if(v[a].lant != v[b].lant)
return v[a].lant < v[b].lant;
return v[a].depth < v[b].depth;
}
int aint[4 * NMAX];
void build(int nod, int st, int dr) {
if(st == dr) {
aint[nod] = v[topo[st]].val;
return;
}
int mid = (st + dr) / 2;
build(2 * nod, st, mid);
build(2 * nod + 1, mid + 1, dr);
aint[nod] = max(aint[2 * nod], aint[2 * nod + 1]);
}
void update(int nod, int st, int dr, int pos, int val) {
if(st == dr) {
aint[nod] = val;
return;
}
int mid = (st + dr) / 2;
if(pos <= mid)
update(2 * nod, st, mid, pos, val);
else
update(2 * nod + 1, mid + 1, dr, pos, val);
aint[nod] = max(aint[2 * nod], aint[2 * nod + 1]);
}
int query(int nod, int st, int dr, int pos1, int pos2) {
if(st == pos1 && dr == pos2)
return aint[nod];
int ans = -1;
int mid = (st + dr) / 2;
if(pos1 <= mid)
ans = query(2 * nod, st, mid, pos1, min(mid, pos2));
if(mid < pos2)
ans = max(ans, query(2 * nod + 1, mid + 1, dr, max(mid + 1, pos1), pos2));
return ans;
}
int solvepath(int a, int b) { ///a-ul e ala de mai JOS
int ans = -1;
while(v[a].lant != v[b].lant) {
int nr1 = l[v[a].lant], nr2 = l[v[b].lant];
if(v[nr1].depth < v[nr2].depth) { ///a-ul sa fie ala pe care il ridicam
swap(nr1, nr2);
swap(a, b);
}
ans = max(ans, query(1, 1, n, v[nr1].pos, v[a].pos));
a = v[nr1].tata;
}
if(v[a].depth < v[b].depth)
swap(a, b);
ans = max(ans, query(1, 1, n, v[b].pos, v[a].pos));
return ans;
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int m;
cin >> n >> m;
for(int i = 1; i <= n; i++)
cin >> v[i].val;
adj.resize(n + 1);
for(int i = 1; i < n; i++) {
int a, b;
cin >> a >> b;
adj[a].push_back(b);
adj[b].push_back(a);
}
v[1].tata = 0;
dfs(1);
for(int i = 1; i <= n; i++)
topo[i] = i;
sort(topo + 1, topo + n + 1, cmp);
for(int i = 1; i <= n; i++) { ///updatam pt fiec pozitia noua din sir
v[topo[i]].pos = i;
}
build(1, 1, n);
while(m--) {
int tip, x, y;
cin >> tip >> x >> y;
if(tip == 0) ///update --> v[x].val = y
update(1, 1, n, v[x].pos, y);
else ///query pe lantul (x, y)
cout << solvepath(x, y) << '\n';
}
return 0;
}