#include <bits/stdc++.h>
using namespace std;
ifstream f("heavypath.in");
ofstream g("heavypath.out");
// #define f cin
// #define g cout
const int nmax = 15e4;
int n, q, albl,
vals[nmax], // valorile initiale
sz[nmax], // dimensiunile subarborilor
label[nmax], // indicii in ordine buna pt aint
invl[nmax], // opusul lui label
top[nmax], // unde se termina lantul
depth[nmax], // distanta de la radacina
pre[nmax]; // parintele nodului
vector<int> v[nmax]; // muchiile
void dfs_init(int, int);
void dfs_heavy(int, int);
class ST
{
int arr[2 * nmax], size = 1;
void build(int ac, int st, int dr)
{
if (dr - st == 1)
{
if (st == 0 || st > n)
arr[ac] = INT_MIN;
else
arr[ac] = vals[invl[st]];
}
else
{
int mid = (st + dr) / 2;
build(ac * 2 + 1, st, mid);
build(ac * 2 + 2, mid, dr);
arr[ac] = max(arr[ac * 2 + 1], arr[ac * 2 + 2]);
}
}
void update(int ac, int st, int dr, int poz, int val)
{
if (dr - st == 1)
arr[ac] = val;
else
{
int mid = (st + dr) / 2;
if (poz < mid)
update(ac * 2 + 1, st, mid, poz, val);
else
update(ac * 2 + 2, mid, dr, poz, val);
arr[ac] = max(arr[ac * 2 + 1], arr[ac * 2 + 2]);
}
}
int query(int ac, int st, int dr, int l, int r)
{
if (st >= r || dr <= l)
return INT_MIN;
if (st >= l && dr <= r)
return arr[ac];
int mid = (st + dr) / 2;
return max(query(ac * 2 + 1, st, mid, l, r), query(ac * 2 + 2, mid, dr, l, r));
}
public:
void init()
{
while (size <= n)
size <<= 1;
build(0, 0, size);
}
void update(int poz, int val)
{
update(0, 0, size, poz, val);
}
int query(int st, int dr)
{
return query(0, 0, size, st, dr + 1);
}
} aint;
int query(int, int);
int32_t main()
{
f >> n >> q;
for (int i = 1; i <= n; i++)
f >> vals[i];
for (int i = 1, x, y; i < n; i++)
f >> x >> y, v[x].push_back(y), v[y].push_back(x);
dfs_init(1, 0);
dfs_heavy(1, 0);
aint.init();
// for (int i = 1; i <= n; i++)
// g << top[i] << '\n';
for (int a, b, c; q; q--)
{
f >> c >> a >> b;
if (c == 0)
aint.update(label[a], b);
else
g << query(a, b) << '\n';
}
return 0;
}
int query(int a, int b)
{
// cout << a << " " << b << '\n';
if (label[a] > label[b])
swap(a, b);
if (top[a] == top[b])
{
return aint.query(label[a], label[b]);
}
if (depth[top[a]] > depth[top[b]])
{
int new_a = pre[top[a]];
return max(aint.query(label[top[a]], label[a]), query(new_a, b));
}
int new_b = pre[top[b]];
return max(aint.query(label[top[b]], label[b]), query(new_b, a));
}
void dfs_init(int ac, int par)
{
pre[ac] = par;
depth[ac] = depth[par] + 1;
top[ac] = ac;
sz[ac] = 1;
for (const int &i : v[ac])
if (i != par)
dfs_init(i, ac), sz[ac] += sz[i];
}
void dfs_heavy(int ac, int par)
{
label[ac] = ++albl;
invl[albl] = ac;
int maxim = -1, ind = -1;
for (const int &i : v[ac])
if (i != par && sz[i] > maxim)
maxim = sz[i], ind = i;
if (maxim == -1)
return;
top[ind] = top[ac];
dfs_heavy(ind, ac);
for (const int &i : v[ac])
if (i != par && i != ind)
dfs_heavy(i, ac);
}