#include <bits/stdc++.h>
using namespace std;
// #pragma GCC optimize("O3")
#pragma GCC target("avx2,fma")
#pragma GCC optimize( "Ofast,unroll-loops" )
static inline int min( int a, int b ) {
return ( a <= b ? a : b );
}
static inline int max( int a, int b ) {
return ( a >= b ? a : b );
}
static inline int mod( int x ) {
return x < 0 ? -x : x;
}
FILE *fin;
int poz, valBuff;
static char buff[ ( 1 << 10 ) ];
static inline char nextChar() {
if( poz == valBuff ) {
fread( buff, 1, valBuff, fin );
poz = 0;
}
return buff[ poz++ ];
}
static bool f[ 100 ];
static inline int readInt() {
int rez = 0;
int ch;
while( !f[ ch = nextChar() ] );
do
rez = rez * 10 + ch - '0';
while( f[ ch = nextChar() ] );
return rez;
}
/////////////////////////////////////////////////////////////////////////////////////////////
const int MAXN = 1e5 + 1;
struct SegmentTree {
int root, left, right;
int SegmentTree[ 2 * MAXN ];
void init( int minVal, int maxVal ) {
root = 1;
left = minVal;
right = maxVal;
}
void update( int nod, int l, int r, int p, const int& x ) {
if ( l == r ) {
SegmentTree[ nod ] = x;
return;
}
int med = ( l + r ) >> 1;
int R = nod + 2 * ( med - l + 1 );
int L = nod + 1;
if( p <= med )
update( L, l, med, p, x );
else update( R, med + 1, r, p, x );
SegmentTree[ nod ] = max( SegmentTree[ L ], SegmentTree[ R ] );
}
void update( int p, const int& x ) {
update( root, left, right, p, x );
}
int query( int nod, int l, int r, int lq, int rq ) {
if( r < lq || l > rq )
return 0;
if( lq <= l && r <= rq )
return SegmentTree[ nod ];
int med = ( l + r ) >> 1;
int R = nod + 2 * ( med - l + 1 );
int L = nod + 1;
return max( query( L, l, med, lq, rq ), query( R, med + 1, r, lq, rq ) );
}
int query( int l, int r ) {
return query( root, left, right, min( l, r ), max( l, r ) );
}
};
struct HEAVY {
int time;
SegmentTree maxx;
vector<int>adj[MAXN];
vector<int> poz, heavy;
vector<int> depth, parent, head;
void add(const int& x, const int& y) {
adj[x].emplace_back(y);
adj[y].emplace_back(x);
}
void init(int size) {
time = 0;
size += 2;
poz.resize(size);
head.resize(size);
depth.resize(size);
heavy.resize(size);
parent.resize(size);
maxx.init(0, size - 1);
dfs();
decompose();
}
int dfs(int nod = 0, int dad = -1) {
int sizeNod = 1;
int sizeNxt = 0;
int maxSize = 0;
heavy[nod] = -1;
parent[nod] = dad;
for(const auto nxt : adj[nod])
if(nxt != dad) {
depth[nxt] = depth[nod] + 1;
sizeNxt = dfs(nxt, nod);
sizeNod += sizeNxt;
if(sizeNxt > maxSize) {
maxSize = sizeNxt;
heavy[nod] = nxt;
}
}
return sizeNod;
}
void decompose(int nod = 0, int dad = -1, int cap = 0) {
head[nod] = cap;
poz[nod] = time++;
if(heavy[nod] != -1)
decompose(heavy[nod], nod, cap);
for(const auto &nxt : adj[nod])
if(nxt != dad && nxt != heavy[nod])
decompose(nxt, nod, nxt);
}
void update(int nod, const int& val) {
maxx.update(poz[nod], val);
}
int query(int a, int b) {
int rez = 0;
while(head[a] != head[b]) {
if(depth[head[a]] > depth[head[b]])
swap(a, b);
rez = max(rez, maxx.query(poz[head[b]], poz[b]));
b = parent[head[b]];
}
if(depth[a] > depth[b])
swap(a, b);
rez = max(rez, maxx.query(poz[a], poz[b]));
return rez;
}
};
int v[MAXN];
HEAVY heavy;
int n, m;
int main()
{
f[ '0' ] = f[ '1' ] = f[ '2' ] = f[ '3' ] = f[ '4' ] = 1;
f[ '5' ] = f[ '6' ] = f[ '7' ] = f[ '8' ] = f[ '9' ] = 1;
valBuff = sizeof( buff );
fin = fopen( "heavypath.in", "r" );
fread( buff, 1, valBuff, fin );
n = readInt();
m = readInt();
for(int i = 0; i < n; i++)
v[i] = readInt();
int x, y;
for(int i = 1; i < n; i++)
heavy.add(readInt() - 1, readInt() - 1);
heavy.init( n );
for( int u = 0; u < n; u++ )
heavy.update( u, v[ u ] );
freopen("heavypath.out", "w", stdout);
bool type;
int a, b;
while(m--) {
type = readInt();
a = readInt();
b = readInt();
if(type)
cout << heavy.query(a - 1, b - 1) << '\n';
else heavy.update(a - 1, b);
}
return 0;
}