#include <bits/stdc++.h>
using namespace std;
ifstream fin("heavypath.in");
ofstream fout("heavypath.out");
const int MaxN = 1e5 + 5;
int n, q, niv[MaxN], what_lant[MaxN];
vector< vector <int> > adj;
vector <int> val;
int sz[MaxN];
int nr_lant;
int poz_lant[MaxN];
struct
{
int father;
vector <int> path;//retin lantul
}lant[MaxN];
void Citire()
{
fin >> n >> q;
val.resize(MaxN);
int i;
for(i = 1; i <= n; ++i)
fin >> val[i];
adj.resize(MaxN);
for(i = 1; i < n; ++i)
{
int a, b;
fin >> a >> b;
adj[a].push_back(b);
adj[b].push_back(a);
}
}
vector< pair <int, int> > maxSon; // sz-ul maxim al unui fiu al nodului i si nodul asociat maximului
//determin sz-ul
int DFS(int node, int d)
{
niv[node] = d;
sz[node] = 1;
for(int i : adj[node])
{
if(sz[i] == 0)// daca nu e vizitat
{
sz[node] += DFS(i, d + 1);
if(maxSon[node].first < sz[i])
maxSon[node].first = sz[i], maxSon[node].second = i;
}
}
return sz[node];
}
void Make_lant(int node, int nr)
{
what_lant[node] = nr;
//indexare de la 1
if(lant[nr].path.empty())
lant[nr].path.push_back(0);
lant[nr].path.push_back(node);
poz_lant[node] = lant[nr].path.size() - 1;
for(int i : adj[node])
{
if(niv[i] < niv[node])
continue;
if(i == maxSon[node].second) // daca am gasit maximul
Make_lant(i, nr);
else
{
++nr_lant;
lant[nr_lant].father = node;
Make_lant(i, nr_lant);
}
}
}
vector < vector <int> > aint; // aint[i] - arborele de intervale asociat lantului i
void Build(int nr, int node, int st, int dr)
{
if(st == dr)
aint[nr][node] = val[lant[nr].path[st]];
else
{
int mij = (st + dr) / 2;
Build(nr, 2 * node, st, mij);
Build(nr, 2 * node + 1, mij + 1, dr);
aint[nr][node] = max(aint[nr][2 * node], aint[nr][2 * node + 1]);
}
}
void Init()
{
int i;
aint.resize(nr_lant + 2);
for(i = 1; i <= nr_lant; ++i)
{
aint[i].resize(1ll << int(log2(lant[i].path.size()) + 2));
Build(i, 1, 1, lant[i].path.size() - 1);
}
}
void Update(int nr, int node, int st, int dr, int poz, int val)
{
if(st == dr && st == poz)
aint[nr][node] = val;
else
{
int mij = (st + dr) / 2;
if(poz <= mij)
Update(nr, 2 * node, st, mij, poz, val);
else Update(nr, 2 * node + 1, mij + 1, dr, poz, val);
aint[nr][node] = max(aint[nr][2 * node], aint[nr][2 * node + 1]);
}
}
int Query(int nr, int node, int st, int dr, int x, int y)
{
if(x <= st && dr <= y)
return aint[nr][node];
else
{
int mij = (st + dr) / 2, val1, val2;
val1 = val2 = INT_MIN;
if(x <= mij)
val1 = Query(nr, 2 * node, st, mij, x, y);
if(y > mij)
val2 = Query(nr, 2 * node + 1, mij + 1, dr, x, y);
return max(val1, val2);
}
}
int Solve(int x, int y)
{
if(what_lant[x] == what_lant[y])
{
int aux = Query(what_lant[x], 1, 1, lant[what_lant[x]].path.size() - 1, min(poz_lant[x], poz_lant[y]), max(poz_lant[x], poz_lant[y]));
return aux;
}
if(niv[lant[what_lant[x]].father] < niv[lant[what_lant[y]].father])
swap(x, y);
int val1 = Query(what_lant[x], 1, 1, lant[what_lant[x]].path.size() - 1, 1, poz_lant[x]);
int val2 = Solve(lant[what_lant[x]].father, y);
return max(val1, val2);
}
int main()
{
Citire();
maxSon.resize(MaxN);
DFS(1, 1);
maxSon.clear();
++nr_lant;
lant[1].father = 0;
Make_lant(1, 1);
adj.clear();
Init();
int query_type, x, y;
for(int i = 1; i <= q; ++i)
{
fin >> query_type >> x >> y;
if(query_type == 0)//update in aint, indicele x devine y
{
Update(what_lant[x], 1, 1, lant[what_lant[x]].path.size() - 1, poz_lant[x], y);
}
else
{
fout << Solve(x, y) << '\n';
}
}
return 0;
}