/*
____ ___ _ ___ ____ _
/ ___| / _ \| | |_ _/ ___| / \
\___ \| | | | | | | | / _ \
___) | |_| | |___ | | |___ / ___ \
|____/ \___/|_____|___\____/_/ \_\
*/
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define pii pair<int, int>
const int NMAX = 1e5 + 9;
const int MOD = 1e9 + 7;
ifstream fin ("heavypath.in");
ofstream fout ("heavypath.out");
#define cin fin
#define cout fout
int binpow(int n, int k)
{
if (k == 0)
{
return 1;
}
int x = binpow(n, k / 2) % MOD;
if (k % 2 == 0)
{
return x * x % MOD;
}
else
{
return x * x % MOD * n % MOD;
}
}
int n, q, i, j;
int v[NMAX];
vector<int> g[NMAX];
int subtree_size[NMAX];
int heavyparent[NMAX];
int heavychild[NMAX];
int in[NMAX], out[NMAX], etour[NMAX * 2];
int minchaintimer[NMAX], maxchaintimer[NMAX];
int p[NMAX];
int timer;
int type, x, y;
int dpt[NMAX];
struct segment_tree
{
int tree[NMAX * 8];
void build(int node, int st, int dr)
{
if (st == dr)
{
tree[node] = etour[st];
}
else
{
int mij = (st + dr) / 2;
build(node * 2, st, mij);
build(node * 2 + 1, mij + 1, dr);
tree[node] = max(tree[node * 2], tree[node * 2 + 1]);
}
}
void update(int node, int st, int dr, int poz, int val)
{
if (st == dr)
{
tree[node] = val;
}
else
{
int mij = (st + dr) / 2;
if (poz <= mij)
{
update(node * 2, st, mij, poz, val);
}
else
{
update(node * 2 + 1, mij + 1, dr, poz, val);
}
tree[node] = max(tree[node * 2], tree[node * 2 + 1]);
}
}
int query(int node, int st, int dr, int l, int r)
{
if (l <= st && dr <= r)
{
return tree[node];
}
else
{
int mij = (st + dr) / 2;
if (r <= mij)
{
return query(node * 2, st, mij, l, r);
}
if (l > mij)
{
return query(node * 2 + 1, mij + 1, dr, l, r);
}
return max(query(node * 2, st, mij, l, r), query(node * 2 + 1, mij + 1, dr, l, r));
}
}
} aint;
void find_subtree_size(int node, int depth = 0, int parent = 0)
{
dpt[node] = depth;
p[node] = parent;
subtree_size[node] = 1;
for (auto it : g[node])
{
if (it == parent)
{
continue;
}
find_subtree_size(it, depth + 1, node);
subtree_size[node] += subtree_size[it];
}
}
void dfsheavy(int node, int parent = 0)
{
pii maxi = {0, 0};
for (auto it : g[node])
{
if (it == parent)
{
continue;
}
maxi = max(maxi, {subtree_size[it], it});
}
heavychild[node] = maxi.second;
if (!heavyparent[node])
{
heavyparent[node] = node;
}
heavyparent[heavychild[node]] = heavyparent[node];
for (auto it : g[node])
{
if (it == parent)
{
continue;
}
dfsheavy(it, node);
}
}
void eulertour(int node, int parent = 0)
{
in[node] = ++timer;
minchaintimer[heavyparent[node]] = min(minchaintimer[heavyparent[node]], timer);
maxchaintimer[heavyparent[node]] = max(maxchaintimer[heavyparent[node]], timer);
etour[timer] = v[node];
if (heavychild[node])
{
eulertour(heavychild[node], node);
}
for (auto it : g[node])
{
if (it == parent || it == heavychild[node])
{
continue;
}
eulertour(it, node);
}
out[node] = ++timer;
etour[timer] = v[node];
}
void query(int a, int b)
{
int answer = 0;
int op=0;
while (1)
{
if (heavyparent[a] == heavyparent[b])
{
answer = max(answer, aint.query(1, 1, timer, min(in[a], in[b]), max(in[a], in[b])));
break;
}
else
{
if (dpt[heavyparent[a]] > dpt[heavyparent[b]])
{
answer = max(answer, aint.query(1, 1, timer, in[heavyparent[a]], in[a]));
a = p[heavyparent[a]];
}
else
{
answer = max (answer,aint.query (1,1,timer,in[heavyparent[b]],in[b]));
b=p[heavyparent[b]];
}
}
}
cout<<answer<<'\n';
}
void run_case()
{
cin >> n >> q;
for (i = 1; i <= n; i++)
{
cin >> v[i];
}
for (i = 1; i < n; i++)
{
int a, b;
cin >> a >> b;
g[a].pb(b), g[b].pb(a);
}
find_subtree_size(1);
dfsheavy(1);
eulertour(1);
aint.build(1, 1, timer);
while (q--)
{
cin >> type >> x >> y;
if (type == 0)
{
aint.update(1, 1, timer, in[x], y);
}
else
{
query(x, y);
}
}
}
signed main()
{
ios_base::sync_with_stdio(0);
cin.tie(NULL), cout.tie(NULL);
int teste;
teste = 1;
while (teste--)
{
run_case();
}
}