#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
typedef int var;
#define MAXN 100005
#define INF (1 << 30)
#define mid(a, b) ((a+b)>>1)
var HEAVY[MAXN], D[MAXN], VAL[MAXN];
var In[MAXN], PathP[MAXN], N[MAXN], Poz[MAXN];
var paths;
vector<var> Paths[MAXN];
vector<var> G[MAXN];
var n;
void dfs(var node) {
HEAVY[node] = 1;
var M = -1;
var choose = -1;
for(auto vec : G[node]) {
if(!HEAVY[vec]) {
D[vec] = D[node] + 1;
dfs(vec);
HEAVY[node] += HEAVY[vec];
PathP[In[vec]] = node;
if(M < HEAVY[vec]) {
choose = In[vec];
M = HEAVY[vec];
}
}
}
if( M == -1 ) {
Paths[paths].push_back(node);
In[node] = paths;
paths ++;
} else {
Paths[choose].push_back(node);
In[node] = choose;
}
}
class SegmentTree {
var *TREE;
var n;
var ind;
var qb, qe;
var _poz;
var _r;
public:
SegmentTree(var ind) {
this->ind = ind;
this->n = N[ind];
TREE = new var[4*n];
build_tree(1, 1, n);
}
SegmentTree() {}
void build_tree(var node, var b, var e) {
if(b == e) {
TREE[node] = VAL[Paths[ind][b]];
return;
}
var m = mid(b, e);
build_tree(node<<1, b, m);
build_tree(node<<1|1, m+1, e);
TREE[node] = max(TREE[node<<1], TREE[node<<1|1]);
}
void update(var node, var b, var e) {
if(b == e) {
TREE[node] = VAL[Paths[ind][b]];
return;
}
var m = mid(b, e);
if(_poz <= m)
update(node<<1, b, m);
else
update(node<<1|1, m+1, e);
TREE[node] = max(TREE[node<<1], TREE[node<<1|1]);
}
void doUpdate(var poz) {
_poz = poz;
update(1, 1, n);
}
void query(var node, var b, var e) {
if(b >= qb && e <= qe) {
_r = max(_r, TREE[node]);
return;
}
if(b > qe || e < qb) {
return;
}
var m = mid(b, e);
if(qb <= m)
query(node<<1, b, m);
if(qe > m)
query(node<<1|1, m+1, e);
}
var getQuery(var l, var r) {
qb = l;
qe = r;
_r = -INF;
query(1, 1, n);
return _r;
}
};
SegmentTree Trees[MAXN];
int main() {
freopen("heavypath.in", "r", stdin);
freopen("heavypath.out", "w", stdout);
var m, a, b, q, type;
scanf("%d%d", &n, &q);
for(var i=1; i<=n; i++) {
scanf("%d", &VAL[i]);
}
m = n-1;
while(m--) {
scanf("%d%d", &a, &b);
G[a].push_back(b);
G[b].push_back(a);
}
dfs(1);
PathP[In[1]] = 0;
for(var i=0; i<paths; i++) {
Paths[i].push_back(0);
reverse(Paths[i].begin(), Paths[i].end());
N[i] = Paths[i].size() - 1;
Trees[i] = SegmentTree(i);
for(var j=1; j<=N[i]; j++) {
Poz[Paths[i][j]] = j;
}
}
while(q--) {
scanf("%d%d%d", &type, &a, &b);
if(type == 0) {
VAL[a] = b;
Trees[In[a]].doUpdate(Poz[a]);
} else {
var p1 = In[a],
p2 = In[b];
var rez = -INF;
while(p1 != p2) {
if(D[PathP[p1]] > D[PathP[p2]]){
rez = max(rez, Trees[p1].getQuery(1, Poz[a]));
a = PathP[p1];
p1 = In[a];
} else {
rez = max(rez, Trees[p2].getQuery(1, Poz[b]));
b = PathP[p2];
p2 = In[b];
}
}
if(Poz[a] > Poz[b])
swap(a, b);
rez = max(rez, Trees[p1].getQuery(Poz[a], Poz[b]));
printf("%d\n", rez);
}
}
return 0;
}