#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 vertex[nmax], aint[4 * nmax], perm_heavy[nmax], lca[MAXLOG][nmax], n;
struct structura_sortare{
int v;
bool operator < (const structura_sortare & x ) const {
if( nod[v].size >= nod[x.v].size )
return true;
return false;
}
};
vector <structura_sortare> 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( structura_sortare fiu: edges[vf] ) {
if( fiu.v == parinte )
continue;
find_size( fiu.v, vf, lev + 1 );
nod[vf].size += nod[fiu.v].size;
}
}
int timp = 1;
void find_heavy( int vf, int next ) {
bool fiu_heavy_gasit = false;
nod[vf].time = timp;
timp++;
nod[vf].next = next;
for( int i = 0; i < edges[vf].size(); i++ ) {
if( edges[vf][i].v != nod[vf].parent ) {
if( !fiu_heavy_gasit ) {
fiu_heavy_gasit = true;
find_heavy( edges[vf][i].v, next );
} else
find_heavy( edges[vf][i].v, edges[vf][i].v );
}
}
}
int urca_niv( int vf, int niv ) {
int ind_p2 = 0;
while( niv > 0 ) {
if( niv % 2 == 1 )
vf = lca[ind_p2][vf];
ind_p2++;
niv /= 2;
}
return vf;
}
int find_lca( int a, int b ) {
int lev_a = nod[a].level, lev_b = nod[b].level;
if( lev_a > lev_b )
a = urca_niv( a, lev_a - lev_b );
else
b = urca_niv( b, lev_b - lev_a );
int i = MAXLOG - 1;
while( i >= 0 && a != b ) {
if( lca[i][a] != lca[i][b] ) {
a = lca[i][a];
b = lca[i][b];
}
i--;
}
if( a != b )
return lca[0][a];
return a;
}
int query_heavy( int a, int b ) {
// level[a] > level[b]
int rez = 0;
while( nod[a].next != nod[b].next ) {
//cout << a << " " << nod[a].next << " ";
//cout << nod[a].time << " " << nod[nod[a].next].time << "\n";
rez = max( rez, query( 1, 1, n, nod[nod[a].next].time, nod[a].time ) );
a = nod[nod[a].next].parent;
}
//cout << "a " << a << " " << b << "\n";
rez = max( rez, query( 1, 1, n, nod[b].time, nod[a].time ) );
return rez;
}
int main() {
ifstream cin("heavypath.in" );
ofstream cout("heavypath.out" );
int m, t, a, b;
int max1, max2;
structura_sortare x, y;
cin >> n >> m;
for( int i = 1; i <= n; i++ )
cin >> vertex[i];
for( int i = 1; i < n; i++ ) {
cin >> x.v >> y.v;
edges[x.v].push_back(y);
edges[y.v].push_back(x);
}
for( int i = 1; i < n; i++ )
sort( edges[i].begin(), edges[i].end() );
find_size( 1, 0, 1 );
find_heavy( 1, 1 );
cout << "\n";
for( int i = 1; i <= n; i++ ) {
perm_heavy[nod[i].time] = vertex[i];
//cout << nod[i].time << " " << vertex[i] << "\n";
lca[0][i] = nod[i].parent;
}
for( int i = 1; i < MAXLOG; i++ )
for( int j = 1; j <= n; j++ )
lca[i][j] = lca[i - 1][lca[i - 1][j]];
init( 1, 1, n );
for( int i = 0; i < m; i++ ) {
cin >> t >> a >> b;
if( t == 0 ) {
vertex[a] = b;
perm_heavy[nod[a].time] = b;
update(1, 1, n, nod[a].time );
} else {
max1 = query_heavy( a, find_lca(a, b) );
max2 = query_heavy( b, find_lca(a, b) );
cout << max( max1, max2 ) << "\n";
}
}
return 0;
}