#include <bits/stdc++.h>
using namespace std;
using uint = unsigned int;
using ll = long long;
using ld = long double;
using ull = unsigned long long;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
#define dbg(...) cerr << #__VA_ARGS__ << " ->", debug_out(__VA_ARGS__)
#define dbg_p(x) cerr<<#x": "<<(x).first<<' '<<(x).second<<endl
#define dbg_v(x, n) {cerr<<#x"[]: ";for(long long _=0;_<n;++_)cerr<<(x)[_]<<' ';cerr<<endl;}
#define all(v) v.begin(), v.end()
#define fi first
#define se second
void debug_out() { cerr << endl; }
template <typename Head, typename... Tail> void debug_out(Head H, Tail... T) { cerr << " " << H; debug_out(T...);}
template<typename T1, typename T2>
ostream& operator <<(ostream &out, const pair<T1, T2> &item) {
out << '(' << item.first << ", " << item.second << ')';
return out;
}
template <typename T>
ostream& operator <<(ostream &out, const vector<T>& v) {
for(const auto &item : v) out << item << ' ';
return out;
}
struct SegTree {
int n;
vector<int> st;
SegTree(int n) : n(n), st(2 * n) {}
void update(int p, int val) {
for(st[p += n] = val; p > 1; p >>= 1) st[p >> 1] = max(st[p], st[p ^ 1]);
}
int query(int l, int r) {
int res = 0;
for(l += n, r += n; l < r; l >>= 1, r >>= 1) {
if(l & 1) res = max(res, st[l++]);
if(r & 1) res = max(res, st[--r]);
}
return res;
}
};
struct HLD {
int n, t;
vector<int> in, out, head, fth, h, sz;
vector<vector<int>> adj;
SegTree segTree;
// in[i] = time entering node i
// out[i] = time leaving node i
// head[i] = head of path containing node i
// fth[i] = parent of node i in original tree
// h[i] = height of node i in original tree starting from 0
// sz[i] = size of subtree of i in original tree
HLD(int n) : n(n), in(n), out(n), head(n), fth(n), h(n), sz(n), adj(n), segTree(n) {}
void addEdge(int a, int b) {
adj[a].push_back(b);
adj[b].push_back(a);
}
void dfsSize(int v, int p = -1) {
sz[v] = 1;
for(auto &u : adj[v])
if(u != p) {
fth[u] = v;
h[u] = 1 + h[v];
dfsSize(u, v);
sz[v] += sz[u];
if(sz[u] > sz[adj[v][0]]) swap(u, adj[v][0]);
}
}
void dfsHld(int v, int p = -1) {
in[v] = t++;
for(auto u : adj[v])
if(u != p) {
head[u] = (u == adj[v][0] ? head[v] : u);
dfsHld(u, v);
}
out[v] = t;
}
void build(const vector<int> &v) {
t = 0;
sz.assign(n, 0);
dfsSize(0);
dfsHld(0);
for(int i = 0; i < n; ++i) segTree.update(in[i], v[i]);
}
void update(int v, int val) {
segTree.update(in[v], val);
}
int query(int v, int u) {
int res = 0;
while(head[v] != head[u]) {
if(h[head[v]] > h[head[u]]) swap(v, u);
res = max(res, segTree.query(in[head[u]], in[u] + 1));
u = fth[head[u]];
}
if(h[v] > h[u]) swap(v, u);
res = max(res, segTree.query(in[v], in[u] + 1));
return res;
}
// subtree of v: [in_v, out_v)
// path from v to the heavy path head: [in_head_v, in_v]
};
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
freopen("heavypath.in", "r", stdin);
freopen("heavypath.out", "w", stdout);
int n, m, a, b, t, x, y;
cin >> n >> m;
vector<int> v(n);
for(int i = 0; i < n; ++i) cin >> v[i];
HLD hld(n);
for(int i = 1; i < n; ++i) {
cin >> a >> b; --a; --b;
hld.addEdge(a, b);
}
hld.build(v);
while(m--) {
cin >> t >> x >> y;
if(t == 0) hld.update(x - 1, y);
else {
cout << hld.query(x - 1, y - 1) << '\n';
}
}
return 0;
}