#include <fstream>
#include <vector>
#include <algorithm>
using namespace std;
ifstream fin ("heavypath.in");
ofstream fout ("heavypath.out");
int n, m, nr_l;
int V[100010], W[100010], Level[100010], L[100010];
int L_Tata[100010], L_Level[100010], L_Dim[100010], L_Pos[100010];
int AINT[400010];
bool F[100010];
vector < int > G[100010], Path[100010];
void DFS(int node)
{
F[node] = true;
W[node] = 1;
int best_chain = -1, leaf = 1;
for (vector < int > :: iterator it = G[node].begin(); it != G[node].end(); it ++)
{
if (!F[*it])
{
leaf = 0;
Level[*it] = Level[node] + 1;
DFS(*it);
W[node] += W[*it];
if (best_chain < 0 || W[*it] > W[best_chain])
{
best_chain = *it;
}
}
}
if (leaf)
{
nr_l ++;
L[node] = nr_l;
L_Dim[nr_l] = 1;
Path[nr_l].push_back(node);
}
else
{
L[node] = L[best_chain];
L_Dim[L[node]] += 1;
Path[L[node]].push_back(node);
for (vector < int > :: iterator it = G[node].begin(); it != G[node].end(); it ++)
{
if (*it != best_chain && Level[*it] > Level[node])
{
L_Tata[L[*it]] = node;
L_Level[L[*it]] = Level[node];
}
}
}
}
inline int Maxim(int const &a, int const &b)
{
if (a > b) return a;
return b;
}
void Build_Tree(int node, int left, int right, int decalaj, int lant)
{
if (left == right)
{
AINT[node + decalaj] = V[Path[lant][left - 1]];
}
else
{
int mid = (left + right) / 2;
Build_Tree(node * 2, left, mid, decalaj, lant);
Build_Tree(node * 2 + 1, mid + 1, right, decalaj, lant);
AINT[node + decalaj] = Maxim(AINT[node * 2 + decalaj], AINT[node * 2 + 1 + decalaj]);
}
}
void Make_Paths()
{
Level[1] = 1;
DFS(1);
for (int i = 1; i <= nr_l; i ++)
{
reverse(Path[i].begin(), Path[i].end());
if (i > 1) L_Pos[i] = L_Pos[i - 1] + L_Dim[i - 1] * 4;
Build_Tree(1, 1, L_Dim[i], L_Pos[i], i);
}
}
void Update(int node, int left, int right, int pos, int val, int decalaj)
{
if (left == right)
{
AINT[node + decalaj] = val;
}
else
{
int mid = (left + right) / 2;
if (pos <= mid) Update(node * 2, left, mid, pos, val, decalaj);
else Update(node * 2 + 1, mid + 1, right, pos, val, decalaj);
AINT[node + decalaj] = Maxim(AINT[node * 2 + decalaj], AINT[node * 2 + 1 + decalaj]);
}
}
int Query(int node, int left, int right, int a, int b, int decalaj)
{
if (a <= left && right <= b)
{
return AINT[node + decalaj];
}
else
{
int mid = (left + right) / 2, ret1 = 0, ret2 = 0;
if (a <= mid) ret1 = Query(node * 2, left, mid, a, b, decalaj);
if (b > mid) ret2 = Query(node * 2 + 1, mid + 1, right, a, b, decalaj);
return Maxim(ret1, ret2);
}
}
int main()
{
fin >> n >> m;
for (int i = 1; i <= n; i ++) fin >> V[i];
for (int a, b, i = 1; i < n; i ++)
{
fin >> a >> b;
G[a].push_back(b);
G[b].push_back(a);
}
Make_Paths();
for (int t, a, b, i = 1; i <= m; i ++)
{
fin >> t >> a >> b;
if (!t)
{
Update(1, 1, L_Dim[L[a]], Level[a] - L_Level[L[a]], b, L_Pos[L[a]]);
}
else
{
int sol = 0;
while (L[a] != L[b])
{
if (L_Level[L[a]] < L_Level[L[b]])
{
a ^= b ^= a ^= b;
}
sol = Maxim(sol, Query(1, 1, L_Dim[L[a]], 1, Level[a] - L_Level[L[a]], L_Pos[L[a]]));
a = L_Tata[L[a]];
}
if (Level[a] > Level[b])
{
a ^= b ^= a ^= b;
}
sol = Maxim(sol, Query(1, 1, L_Dim[L[a]], Level[a] - L_Level[L[a]], Level[b] - L_Level[L[a]], L_Pos[L[a]]));
fout << sol << '\n';
}
}
fout.close();
return 0;
}