#include <fstream>
#include <vector>
#include <algorithm>
using namespace std;
ifstream in("heavypath.in");
ofstream out("heavypath.out");
const int MAXN = 1e5;
const int MAXM = 1e5;
const int MAXVAL = 2e9;
int n, m;
vector<int> g[MAXN + 1];
class Node{
public :
int val;
int sub;
int niv;
int par;
int pat;
int pos;
Node() {
val = 0;
sub = 0;
niv = 0;
par = 0;
pat = 0;
pos = 0;
}
};
vector<Node> nodes(MAXN + 1);
class Path{
public :
vector<int> P, T;
int par;
Path() {
P.clear();
T.clear();
par = 0;
}
void update(int node, int st, int dr, int poz, int val) {
if (st == dr) {
T[node] = val;
return;
}
int mij = (st + dr) / 2;
if (poz <= mij) update(node << 1, st, mij, poz, val);
else update((node << 1) | 1, mij + 1, dr, poz, val);
T[node] = max(T[node << 1], T[(node << 1) | 1]);
}
int query(int node, int st, int dr, int a, int b) {
if (a <= st && dr <= b) return T[node];
int mij = (st + dr) / 2;
int le = 0, ri = 0;
if (a <= mij) le = query(node << 1, st, mij, a, b);
if (mij < b) ri = query((node << 1) | 1, mij + 1, dr, a, b);
return max(le, ri);
}
void addToPath(int node) {
P.push_back(node);
}
void reversePathAndMakeTree() {
reverse(P.begin(), P.end());
T.resize(P.size() << 2);
for (int i = 1; i <= P.size(); ++ i) {
nodes[P[i - 1]].pos = i;
update(1, 1, P.size(), nodes[P[i - 1]].pos, nodes[P[i - 1]].val);
}
}
void showPath() {
for (auto x : P)
out << x << ' ';
out << '\n';
for (int i = 1; i <= P.size(); ++ i) out << query(1, 1, P.size(), i, i) << ' ';
out << '\n';
}
};
vector<Path> paths;
void makePaths(int node, int prev, int niv) {
nodes[node].niv = niv;
nodes[node].par = prev;
int connect = 0, maxsub = 0;
for (auto x : g[node]) {
if (x == prev) continue;
makePaths(x, node, niv + 1);
nodes[node].sub += nodes[x].sub + 1;
if (maxsub < nodes[x].sub + 1) {
maxsub = nodes[x].sub;
connect = x;
}
}
if (connect) {
nodes[node].pat = nodes[connect].pat;
paths[nodes[node].pat].par = prev;
paths[nodes[node].pat].addToPath(node);
}
else {
Path aux;
aux.addToPath(node);
aux.par = prev;
nodes[node].pat = paths.size();
paths.push_back(aux);
}
}
void reversePathsAndMakeTrees() {
for (auto &x : paths)
x.reversePathAndMakeTree();
}
void solve0(int a, int b) {
nodes[a].val = b;
paths[nodes[a].pat].update(1, 1, paths[nodes[a].pat].P.size(), nodes[a].pos, nodes[a].val);
}
int solve1(int a, int b) {
if (nodes[paths[nodes[a].pat].par].niv < nodes[paths[nodes[b].pat].par].niv)
return solve1(b, a);
if (nodes[paths[nodes[a].pat].par].pat == nodes[b].pat)
return max(paths[nodes[a].pat].query(1, 1, paths[nodes[a].pat].P.size(), 1, nodes[a].pos), paths[nodes[b].pat].query(1, 1, paths[nodes[b].pat].P.size(), 1, nodes[b].pos));
else
return max(paths[nodes[a].pat].query(1, 1, paths[nodes[a].pat].P.size(), 1, nodes[a].pos), solve1(paths[nodes[a].pat].par, b));
}
int main() {
in >> n >> m;
for (int i = 1; i <= n; ++ i) in >> nodes[i].val;
for (int i = 1; i < n; ++ i) {
int a, b;
in >> a >> b;
g[a].push_back(b);
g[b].push_back(a);
}
makePaths(1, 1, 1);
reversePathsAndMakeTrees();
int t, a, b;
for (int i = 1; i <= m; ++ i) {
in >> t >> a >> b;
if (t)
out << solve1(a, b) << '\n';
else
solve0(a, b);
}
// for (auto x : paths) x.showPath();
return 0;
}