#include <bits/stdc++.h>
#define vi vector<int>
#define vvi vector<vi>
#define FOR(i, a, b) for(int i = a; i <= b; ++i)
#define FORR(i, a, b) for(int i = a; i >= b; --i)
#define pb push_back
using namespace std;
const string TASK("heavypath");
ifstream fin(TASK + ".in");
ofstream fout(TASK + ".out");
#define cin fin
#define cout fout
const int N = 1e5 + 9;
int n, m, v[N];
vvi G(N);
int tree[2 * N], t[N], dep[N], w[N], heavy_son[N], head[N], pos[N];
void Update(int p, int val)
{
for(tree[p += n] = val; p > 1; p >>= 1)tree[p >> 1] = max(tree[p], tree[p ^ 1]);
}
int Query(int l, int r)
{
int res = INT_MIN;
for(l += n, r += n; l <= r; l >>= 1, r >>= 1)
{
if(l & 1)res = max(res, tree[l ++]);
if(!(r & 1))res = max(res, tree[r --]);
}
return res;
}
int Answer(int a, int b)
{
int res = INT_MIN;
for(; head[a] != head[b]; a = t[head[a]])
{
if(dep[head[a]] < dep[head[b]])swap(a, b);
res = max(res, Query(pos[head[a]], pos[a]));
}
if(dep[a] > dep[b])swap(a, b);
res = max(res, Query(pos[a], pos[b]));
return res;
}
void Dfs(int x)
{
w[x] = 1;
heavy_son[x] = -1;
for(auto y : G[x])
{
if(y == t[x])continue;
t[y] = x;
dep[y] = dep[x] + 1;
Dfs(y);
w[x] += w[y];
if(heavy_son[x] == -1 || w[heavy_son[x]] < w[y])
heavy_son[x] = y;
}
}
int position;
void Decompose(int x, int hd)
{
head[x] = hd;
pos[x] = position++;
Update(pos[x], v[x]);
if(heavy_son[x] != -1)Decompose(heavy_son[x], hd);
for(auto y : G[x])
if(y != heavy_son[x] && y != t[x])
Decompose(y, y);
}
int main()
{
ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> m;
FOR(i, 1, n)cin >> v[i];
int a, b;
FOR(i, 2, n)
{
cin >> a >> b;
G[a].pb(b);
G[b].pb(a);
}
Dfs(1);
Decompose(1, 1);
int op, x, y;
FOR(i, 1, m)
{
cin >> op >> x >> y;
if(op == 0)Update(pos[x], y);
else cout << Answer(x, y) << '\n';
}
return 0;
}