#include <bits/stdc++.h>
using namespace std;
ifstream in("heavypath.in");
ofstream out("heavypath.out");
const int nmax = 100005;
int n,m,v[nmax],parent[nmax],depth[nmax],Size[nmax],heavy[nmax],pos,paths[nmax],headval[nmax];
int head[nmax],posInPaths[nmax],lowest[nmax];
vector<int> g[nmax];
void dfs(int node) {
int maxSize = 0;
for (auto k : g[node]) {
if (k == parent[node]) continue;
parent[k] = node;
depth[k] = depth[node] + 1;
dfs(k);
Size[node] += Size[k];
if (Size[k] > maxSize) {
maxSize = Size[k];
heavy[node] = k;
}
}
Size[node]++;
}
void decomp(int node, int curHead) {
paths[++pos] = node;
posInPaths[node] = pos;
head[node] = curHead;
lowest[head[node]] = node;
if (heavy[node] != 0) {
decomp(heavy[node] , curHead);
}
for (auto k : g[node]) {
if (k == parent[node]) continue;
if (k != heavy[node]) {
decomp(k , k);
}
}
}
struct SegTree {
int l,r,val;
SegTree *lChild, *rChild;
SegTree(int l, int r) : l(l) , r(r) {
if (l == r) {
val = v[paths[l]];
} else {
int mij = (l + r) / 2;
lChild = new SegTree(l, mij);
rChild = new SegTree(mij + 1, r);
val = max(lChild -> val , rChild -> val);
}
}
void update(int qpos, int newVal) {
if (qpos == l && qpos == r) {
val = newVal;
return;
}
if (qpos < l || qpos > r) return;
lChild -> update(qpos, newVal);
rChild -> update(qpos, newVal);
val = max(lChild -> val , rChild -> val);
}
int query(int ql, int qr) {
if (ql <= l && r <= qr) {
return val;
}
if (l > qr || r < ql) {
return 0;
}
return max( lChild -> query(ql, qr) , rChild -> query(ql, qr) );
}
SegTree() {}
};
SegTree segTree;
int pathQuerry(int u, int v) {
int Max = -1;
while ( true ) {
if ( depth [ head [ u ] ] > depth [ head [ v ] ] ) swap(u,v); // depth of u's head will be smallest, so v will be lowest, so we bring him up.
if (head[u] != head[v]) { // we bring v up
if (v == lowest[head[v]]) {
Max = max(Max , headval[head[v]]);
} else {
Max = max(Max, segTree.query(posInPaths[head[v]], posInPaths[v]));
}
v = parent[head[v]];
} else {
if (posInPaths[u] > posInPaths[v]) swap(u , v);
Max = max(Max , segTree.query ( posInPaths [ u ] , posInPaths [ v ] ) );
return Max;
}
}
}
int main() {
in >> n >> m;
for (int i = 1; i <= n; i++) {
in >> v[i];
}
for (int i = 1; i < n; i++) {
int u,v;
in >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
depth[1] = 1;
dfs(1);
decomp(1, 1);
for (int i = 0; i <= n; i++) g[i].clear();
segTree = SegTree(1 , n);
for (int i = 1; i <= n; i++) {
if (head[i] == i) {
headval[i] = segTree.query(posInPaths[i] , posInPaths[lowest[i]]);
}
}
for (int i = 1; i <= m; i++) {
int t,x,y;
in >> t >> x >> y;
if (t == 1) {
out << pathQuerry(x , y) << "\n";
}
else {
segTree.update(posInPaths[x] , y);
headval[head[x]] = segTree.query(posInPaths[head[x]] , posInPaths[lowest[head[x]]]);
}
}
return 0;
}