#include <fstream>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 100005;
struct tip {
vector<int> array;
int parent, depth;
};
struct operatie{
int t, x, y;
} op[N];
vector<tip> Paths;
vector< vector<int> > ARB_INT;
int whatPath[N], whatPos[N];
vector<int> a[N];
int t[N], v[N], d[N], DEPTH[N], x[N], n, m;
inline int max(int a, int b) {
return a < b ? b : a;
}
void dfs(int k) {
int i, r = 0;
v[k] = 1;
for(i = 0; i < a[k].size(); ++i)
if(!v[a[k][i]]){
t[a[k][i]] = k;
DEPTH[a[k][i]] = DEPTH[k] + 1;
dfs(a[k][i]);
d[k] += d[a[k][i]];
if(d[r] < d[a[k][i]])
r = a[k][i];
}
if(r == 0){
Paths.resize(Paths.size() + 1);
Paths[Paths.size() - 1].array.push_back(k);
Paths[Paths.size() - 1].parent = k;
whatPath[k] = Paths.size() - 1;
}
else{
Paths[whatPath[r]].array.push_back(k);
Paths[whatPath[r]].parent = k;
whatPath[k] = whatPath[r];
}
}
void update(int val, int Path, int Pos, int st, int dr, int poz){
if(st > Pos)
return ;
if(dr < Pos)
return ;
if(st == dr){
ARB_INT[Path][poz] = val;
return ;
}
int x = (st + dr) / 2;
update(val, Path, Pos, st, x, poz * 2);
update(val, Path, Pos, x + 1, dr, poz * 2 + 1);
ARB_INT[Path][poz] = max(ARB_INT[Path][poz * 2], ARB_INT[Path][poz * 2 + 1]);
}
int query(int p1, int p2, int Path, int st, int dr, int poz){
// if(poz == 1)
// printf("%d %d %d\n", p1, p2, Path);
if(p1 > dr || p2 < st)
return 0;
if(p1 <= st && dr <= p2)
return ARB_INT[Path][poz];
int x = (st + dr) / 2;
int q1 = query(p1, p2, Path, st, x, poz * 2);
int q2 = query(p1, p2, Path, x + 1, dr, poz * 2 + 1);
return max(q1, q2);
}
ofstream fout("heavypath.out");
void solve(int ind) {
int i, j, sol = 0, x, y;
if(op[ind].t == 0)
::x[op[ind].x] = op[ind].y, update(op[ind].y, whatPath[op[ind].x], whatPos[op[ind].x], 0, Paths[whatPath[op[ind].x]].array.size() - 1 , 1);
else
{
x = op[ind].x;
y = op[ind].y;
while(x != y) {
// printf("%d %d %d %d\n", x, y, Paths[whatPath[x]].depth, Paths[whatPath[y]].depth);
if(whatPath[x] == whatPath[y]){
if(DEPTH[x] > DEPTH[y])
swap(x, y);
sol = max(sol, query(whatPos[x], whatPos[y], whatPath[x], 0, Paths[whatPath[x]].array.size() - 1, 1));
x = y;
}
else{
if(Paths[whatPath[x]].depth < Paths[whatPath[y]].depth)
swap(x, y);
sol = max(sol, query(0, whatPos[x], whatPath[x], 0, Paths[whatPath[x]].array.size() - 1, 1));
x = Paths[whatPath[x]].parent;
// printf("%d %d\n", x, y);
}
}
sol = max(sol, ::x[x]);
fout << sol << '\n';
}
}
int main() {
ifstream fin("heavypath.in");
int i, j, X, Y;
fin >> n >> m;
for(i = 1; i <= n; ++i)
fin >> x[i];
for(i = 1; i <= n; ++i)
d[i] = 1;
for(i = 1; i < n ; ++i){
fin >> X >> Y;
a[X].push_back(Y);
a[Y].push_back(X);
}
Paths.reserve(N);
dfs(1);
ARB_INT.reserve(Paths.size());
ARB_INT.resize(Paths.size());
// for(i = 1; i <= n; ++i)
// fout << i << " " << whatPath[i] << '\n';
for(i = 0 ; i < Paths.size(); ++i) {
reverse(Paths[i].array.begin(), Paths[i].array.end());
for(j = 0; j < Paths[i].array.size(); ++j)
whatPos[Paths[i].array[j]] = j;
Paths[i].depth = DEPTH[Paths[i].array[0]];
Paths[i].parent = t[Paths[i].parent];
// fout << i << " " << Paths[i].parent << " " << Paths[i].depth << '\n';
// for(j = 0; j < Paths[i].array.size(); ++j)
// fout << Paths[i].array[j] << '\n';
ARB_INT[i].reserve(Paths[i].array.size() * 4 + 5);
ARB_INT[i].resize(Paths[i].array.size() * 4 + 5);
}
for(i = 1; i <= n; ++i)
update(x[i], whatPath[i],whatPos[i], 0, Paths[whatPath[i]].array.size()- 1, 1);
/*
for(i = 0; i < ARB_INT.size(); ++i){
for(j = 1; j < ARB_INT[i].size(); ++j)
fout <<j << " " << ARB_INT[i][j] << '\n';
fout << '\n';
}
*/
for(i = 1; i <= m; ++i){
fin >> op[i].t >> op[i].x >> op[i].y;
solve(i);
}
return 0;
}