#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
using namespace std;
const int nmax = 1e5 + 1;
const int MAXLOG = 18;
struct info{
int size, next, parent, time, level;
};
info nod[nmax];
int aint[4 * nmax], perm_heavy[nmax], n;
vector <int> val;
vector <int> edges[nmax];
void init( int nod, int l, int r ) {
if( l == r ) {
aint[nod] = perm_heavy[l];
return;
}
init( 2 * nod, l, (l + r) / 2 );
init( 2 * nod + 1, (l + r) / 2 + 1, r );
aint[nod] = max( aint[2 * nod], aint[2 * nod + 1] );
}
int query( int nod, int l, int r, int lq, int rq ) {
if( r < lq || rq < l )
return 0;
if( lq <= l && r <= rq )
return aint[nod];
return max(query(2 * nod, l, (l + r) / 2, lq, rq), query(2 * nod + 1, (l + r) / 2 + 1, r, lq, rq) );
}
void update( int nod, int l, int r, int poz_update ) {
if( poz_update < l || poz_update > r )
return;
if( l == r ) {
aint[nod] = perm_heavy[l];
return;
}
update( 2 * nod, l, (l + r) / 2, poz_update );
update( 2 * nod + 1, (l + r) / 2 + 1, r, poz_update );
aint[nod] = max( aint[2 * nod], aint[2 * nod + 1] );
}
void find_size( int vf, int parinte, int lev ) {
nod[vf].level = lev;
nod[vf].parent = parinte;
nod[vf].size = 1;
if( vf != 1 && edges[vf].size() == 1 )
return;
for( int fiu: edges[vf] ) {
if( fiu == parinte )
continue;
find_size( fiu, vf, lev + 1 );
nod[vf].size += nod[fiu].size;
}
}
int timp = 1;
void find_heavy( int vf, int next ) {
int maxx = 0, max_fiu;
nod[vf].time = timp;
timp++;
nod[vf].next = next;
if( nod[vf].size == 1 )
return;
for( int i = 0; i < edges[vf].size(); i++ ) {
if( edges[vf][i] != nod[vf].parent ) {
if( nod[edges[vf][i]].size > maxx ) {
maxx = nod[edges[vf][i]].size;
max_fiu = edges[vf][i];
}
}
}
find_heavy( max_fiu, next );
for( int i = 0; i < edges[vf].size(); i++ ) {
if( edges[vf][i] != nod[vf].parent && edges[vf][i] != max_fiu )
find_heavy( edges[vf][i], edges[vf][i] );
}
}
int query_heavy( int a, int b ) {
int rez = 0;
while( nod[a].next != nod[b].next ) {
//cout << a << "a " << b << "b\n";
//cout << nod[a].time << " " << nod[nod[a].next].time << "\n";
if( nod[a].level > nod[b].level ) {
rez = max(rez, query(1, 1, n, nod[nod[a].next].time, nod[a].time));
a = nod[nod[a].next].parent;
} else {
rez = max(rez, query(1, 1, n, nod[nod[b].next].time, nod[b].time));
b = nod[nod[b].next].parent;
}
}
//cout << "a " << a << " " << b << "\n";
if( nod[b].time < nod[a].time )
rez = max( rez, query( 1, 1, n, nod[b].time, nod[a].time ) );
else
rez = max( rez, query( 1, 1, n, nod[a].time, nod[b].time ) );
return rez;
}
int main() {
ifstream cin("heavypath.in" );
ofstream cout("heavypath.out" );
int m, t, a, b;
int max1, max2;
int x, y;
cin >> n >> m;
val.resize( n );
for( int i = 0; i < n; i++ )
cin >> val[i];
for( int i = 1; i < n; i++ ) {
cin >> x >> y;
edges[x].push_back(y);
edges[y].push_back(x);
}
find_size( 1, 0, 1 );
find_heavy( 1, 1 );
for( int i = 1; i <= n; i++ )
perm_heavy[nod[i].time] = val[i - 1];
val.clear();
init( 1, 1, n );
for( int i = 0; i < m; i++ ) {
cin >> t >> a >> b;
if( t == 0 ) {
perm_heavy[nod[a].time] = b;
update(1, 1, n, nod[a].time );
} else {
max1 = query_heavy( a, b );
cout << max1 << "\n";
}
}
return 0;
}