#include <bits/stdc++.h>
using namespace std;
void debug_out() { cerr << endl; }
template<class T> ostream& prnt(ostream& out, T v) { out << v.size() << '\n'; for(auto e : v) out << e << ' '; return out;}
template<class T> ostream& operator<<(ostream& out, vector <T> v) { return prnt(out, v); }
template<class T> ostream& operator<<(ostream& out, set <T> v) { return prnt(out, v); }
template<class T1, class T2> ostream& operator<<(ostream& out, map <T1, T2> v) { return prnt(out, v); }
template<class T1, class T2> ostream& operator<<(ostream& out, pair<T1, T2> p) { return out << '(' << p.first << ' ' << p.second << ')'; }
template <typename Head, typename... Tail> void debug_out(Head H, Tail... T) { cerr << " " << H; debug_out(T...);}
#define dbg(...) cerr << #__VA_ARGS__ << " ->", debug_out(__VA_ARGS__)
#define dbg_v(x, n) do{cerr<<#x"[]: ";for(int _=0;_<n;++_)cerr<<x[_]<<" ";cerr<<'\n';}while(0)
#define dbg_ok cerr<<"OK!\n"
#define ll long long
#define ld long double
#define ull unsigned long long
#define pii pair<int,int>
#define MOD 1000000007
#define zeros(x) x&(x-1)^x
#define fi first
#define se second
#define NMAX 100005
const long double PI = acos(-1);
template <class T>
class SegmentTree{
int n;
public:
vector<int> t;
void build() { // build the tree
for (int i = n - 1; i > 0; --i) t[i] = max(t[i<<1], t[i<<1|1]); // pentru suma schimba aici
}
void init(int n){
this->n = n;
t.resize(2 * NMAX);
}
void set(int p, int value) { // set value at position p
for (t[p += n] = value; p > 1; p >>= 1) t[p>>1] = max(t[p], t[p^1]); // pentru suma schimba aici
}
int query(int l, int r) { // max on interval [l, r)
int res = 0;
for (l += n, r += n; l < r; l >>= 1, r >>= 1) {
if (l&1) res = max(res, t[l++]); // pentru suma schimba aici
if (r&1) res = max(res, t[--r]); // pentru suma schimba aici
}
return res;
}
};
template <class T, int V>
class HeavyLight {
int parent[V], heavy[V], depth[V];
int root[V], treePos[V];
SegmentTree<T> tree;
template <class G>
int dfs(const G& graph, int v) {
int size = 1, maxSubtree = 0;
for (int u : graph[v]) if (u != parent[v]) {
parent[u] = v;
depth[u] = depth[v] + 1;
int subtree = dfs(graph, u);
if (subtree > maxSubtree) heavy[v] = u, maxSubtree = subtree;
size += subtree;
}
return size;
}
template <class BinaryOperation>
void processPath(int u, int v, BinaryOperation op) {
for (; root[u] != root[v]; v = parent[root[v]]) {
if (depth[root[u]] > depth[root[v]]) swap(u, v);
op(treePos[root[v]], treePos[v] + 1);
}
if (depth[u] > depth[v]) swap(u, v);
op(treePos[u], treePos[v] + 1);
}
public:
// vector<vector<int>> graph
// indexat de la 0
template <class G>
void init(const G& graph) {
int n = graph.size();
fill_n(heavy, n, -1);
parent[0] = -1;
depth[0] = 0;
dfs(graph, 0);
for (int i = 0, currentPos = 0; i < n; ++i)
if (parent[i] == -1 || heavy[parent[i]] != i)
for (int j = i; j != -1; j = heavy[j]) {
root[j] = i;
treePos[j] = currentPos++;
}
tree.init(n);
}
void set(int v, const T& value) {
tree.set(treePos[v], value);
}
void modifyPath(int u, int v, const T& value) {
processPath(u, v, [this, &value](int l, int r) { tree.modify(l, r, value); });
}
T queryPath(int u, int v) {
T res = T();
processPath(u, v, [this, &res](int l, int r) { res = max(res, tree.query(l, r)); }); // face maxim, daca vrei altceva schimba
return res;
}
};
HeavyLight<int, NMAX> hld;
int n, m, x, v[NMAX];
vector<vector<int>> g;
int main(){
ios::sync_with_stdio(false);
freopen("heavypath.in","r",stdin);
freopen("heavypath.out","w",stdout);
scanf("%d%d",&n,&m);
g.resize(n);
for (int i=0;i<n;i++){
scanf("%d", &v[i]);
}
for (int i=1;i<n;i++){
int y;
scanf("%d%d", &x, &y);
x--;
y--;
g[x].push_back(y);
g[y].push_back(x);
}
hld.init(g);
for (int i=0;i<n;i++){
hld.set(i, v[i]);
}
for (int i=1;i<=m;i++){
int t,y;
scanf("%d%d%d", &t, &x, &y);
if (t == 0){
x--;
hld.set(x, y);
}
else{
x--; y--;
cout << hld.queryPath(x,y) << '\n';
}
}
return 0;
}