// solutie care foloseste heavy path decomposition
using namespace std;
#ifdef EZ
#include "./ez/ez.h"
const string FILE_NAME = "test";
#else
#include <bits/stdc++.h>
const string FILE_NAME = "heavypath";
#endif
#define mp make_pair
#define mt make_tuple
#define ll long long
#define pb push_back
#define eb emplace_back
#define fi first
#define se second
#define cin fin
#define cout fout
ifstream fin (FILE_NAME + ".in");
ofstream fout (FILE_NAME + ".out");
const int nMAX = 100e3;
int n, m;
int v[nMAX + 1];
vector<int> gf[nMAX + 1];
int tat[nMAX + 1];
int niv[nMAX + 1];
int subarbNods[nMAX + 1]; // cate noduri are un subarbore
vector<int> path[nMAX + 1];
vector<int> aint[nMAX + 1];
int firstNodeInPath[nMAX + 1];
void dfs(int nod, int dad = 0)
{
niv[nod] = niv[dad] + 1;
tat[nod] = dad;
subarbNods[nod]++;
for (int fiu : gf[nod])
{
if (fiu == dad)
continue;
dfs(fiu, nod);
subarbNods[nod] += subarbNods[fiu];
}
}
void dfsDecompose(int nod, vector<int> &p, int dad = 0)
{
p.pb(nod);
int max = 0, i;
for (int fiu : gf[nod])
{
if (fiu == dad)
continue;
if (subarbNods[fiu] > max)
{
max = subarbNods[fiu];
i = fiu;
}
}
for (int fiu : gf[nod])
{
if (fiu == dad)
continue;
if (fiu == i)
{
firstNodeInPath[fiu] = firstNodeInPath[nod];
dfsDecompose(fiu, p, nod);
}
else
{
firstNodeInPath[fiu] = fiu;
dfsDecompose(fiu, path[fiu], nod);
}
}
}
void buildAint(int which, int nod, int st, int dr)
{
vector<int> &Aint = aint[which];
vector<int> &V = path[which];
if (st == dr)
return (void) (Aint[nod] = v[V[st-1]]);
int mj = st+dr >> 1;
buildAint(which, nod<<1, st, mj);
buildAint(which, nod<<1|1, mj+1, dr);
Aint[nod] = max(Aint[nod<<1], Aint[nod<<1|1]);
}
int queryAint(vector<int> &aint, int nod, int st, int dr, int qst, int qdr)
{
if (qst <= st && dr <= qdr)
return aint[nod];
int mj = st+dr >> 1;
int ans = 0;
if (qst <= mj)
ans = max(ans, queryAint(aint, nod<<1, st, mj, qst, qdr));
if (mj+1 <= qdr)
ans = max(ans, queryAint(aint, nod<<1|1, mj+1, dr, qst, qdr));
return ans;
}
void updateAint(vector<int> &aint, int nod, int st, int dr, int qpos, int qval)
{
if (st == dr)
return (void) (aint[nod] = qval);
int mj = st+dr >> 1;
if (qpos <= mj)
updateAint(aint, nod<<1, st, mj, qpos, qval);
else
updateAint(aint, nod<<1|1, mj+1, dr, qpos, qval);
aint[nod] = max(aint[nod<<1], aint[nod<<1|1]);
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; ++i)
cin >> v[i];
for (int i = 1; i < n; ++i)
{
int a, b;
cin >> a >> b;
gf[a].pb(b);
gf[b].pb(a);
}
dfs(1);
firstNodeInPath[1] = 1;
dfsDecompose(1, path[1]);
for (int i = 1; i <= n; ++i)
{
if (path[i].empty())
continue;
aint[i].resize(4 * path[i].size());
buildAint(i, 1, 1, path[i].size());
}
while (m--)
{
int op, x, y;
cin >> op >> x >> y;
//cerr << "\nQUERY " << op << ' ' << x << ' ' << y << '\n';
if (op == 0)
{
int first = firstNodeInPath[x];
v[x] = y;
updateAint(aint[first], 1, 1, path[first].size(), niv[x] - niv[first] + 1, y);
}
else if (op == 1)
{
vector<int> tx, ty;
for (int xc = x; xc; )
{
xc = firstNodeInPath[xc];
xc = tat[xc];
tx.pb(xc);
//cerr << tx.back() << ' ';
} //cerr << '\n';
for (int yc = y; yc; )
{
yc = firstNodeInPath[yc];
yc = tat[yc];
ty.pb(yc);
//cerr << ty.back() << ' ';
}
// cerr << '\n';
int lca = 0;
for (int i = tx.size()-1, j = ty.size()-1; i!=-1 && j!=-1; i--, j--)
{
if (tx[i] != ty[j])
{
if (niv[tx[i]] <= niv[ty[j]])
lca = tx[i];
else
lca = ty[j];
break;
}
else
lca = tx[i];
}
if (lca == 0)
lca = 1;
// cerr << "\nLCA OF " << x << ' ' << y << " IS " << lca << '\n';
auto solve = [&](int x, int y) -> int { // x e stramos al lui y
int ans = 0;
while (y)
{
int first = firstNodeInPath[y];
if (niv[first] >= niv[x])
{
ans = max(ans, queryAint(aint[first], 1, 1, path[first].size(),
1, niv[y] - niv[first] + 1));
y = tat[first];
}
else
{
ans = max(ans, queryAint(aint[first], 1, 1, path[first].size(),
niv[x] - niv[first] + 1, niv[y] - niv[first] + 1));
y = 0;
}
}
return ans;
};
if (lca != x && lca != y)
cout << max(solve(lca, x), solve(lca, y)) << '\n';
else if (niv[x] <= niv[y])
cout << solve(x, y) << '\n';
else
cout << solve(y, x) << '\n';
}
}
}