#include <fstream>
#include <vector>
using namespace std;
const int nmax = 1e5;
struct Segment_tree {
private:
vector < int > aint;
int N;
int combine ( int a, int b ) {
return a > b ? a : b;
}
void Update ( int node, int st, int dr, int poz, int val ) {
if ( st == dr ) {
aint[node] = val;
return;
}
int mij = ( st + dr ) / 2;
if ( poz <= mij )
Update ( node + 1, st, mij, poz, val );
else
Update ( node + 2 * ( mij - st + 1 ), mij + 1, dr, poz, val );
aint[node] = combine ( aint[node + 1], aint[node + 2 * ( mij - st + 1 )] );
}
int Query ( int node, int st, int dr, int a, int b ) {
if ( st == a && dr == b )
return aint[node];
int mij = ( st + dr ) / 2;
if ( b <= mij )
return Query ( node + 1, st, mij, a, b );
if ( a > mij )
return Query ( node + 2 * ( mij - st + 1 ), mij + 1, dr, a, b );
return combine ( Query ( node + 1, st, mij, a, mij ),
Query ( node + 2 * ( mij - st + 1 ), mij + 1, dr, mij + 1, b ) );
}
public:
void init ( int n ) {
N = n;
aint.resize ( 2 * N );
}
void update ( int poz, int val ) {
Update ( 0, 0, N - 1, poz, val );
}
int query ( int st, int dr ) {
return Query ( 0, 0, N - 1, min ( st, dr ), max ( st, dr ) );
}
};
struct heavy {
private:
vector < int > parent;
vector < int > heavy;
vector < int > depth;
vector < int > poz;
vector < int > head;
vector < int > g[nmax + 1];
Segment_tree aint;
int dfs ( int node, int par = -1 ) {
int max_size = 0, size = 1;
parent[node] = par;
heavy[node] = -1;
for ( int x: g[node] )
if ( x != par ) {
depth[x] = depth[node] + 1;
int sz = dfs ( x, node );
size += sz;
if ( sz > max_size ) {
max_size = sz;
heavy[node] = x;
}
}
return size;
}
int time = 0;
void decompose ( int node, int par, int Head ) {
head[node] = Head;
poz[node] = time++;
if ( heavy[node] != -1 )
decompose ( heavy[node], node, Head );
for ( int x: g[node] )
if ( x != heavy[node] && x != par )
decompose ( x, node, x );
}
public:
void init ( int n ) {
parent.resize ( n + 1 );
heavy.resize ( n + 1 );
depth.resize ( n + 1 );
poz.resize ( n + 1 );
head.resize ( n + 1 );
aint.init ( n );
}
void add_edge ( int x, int y ) {
g[x].push_back ( y );
g[y].push_back ( x );
}
void update ( int node, int val ) {
aint.update ( poz[node], val );
}
int query ( int a, int b ) {
int ans = 0;
while ( head[a] != head[b] ) {
if ( depth[head[a]] < depth[head[b]] )
swap ( a, b );
ans = max ( ans, aint.query ( poz[a], poz[head[a]] ) );
a = parent[head[a]];
}
ans = max ( ans, aint.query ( poz[a], poz[b] ) );
return ans;
}
void build ( int node = 1 ) {
dfs ( node );
decompose ( node, -1, node );
}
} heavy;
int v[nmax + 1];
ifstream fin ( "heavypath.in" );
ofstream fout ( "heavypath.out" );
int main() {
int n, q, x, y, t;
fin >> n >> q;
for ( int i = 1; i <= n; i++ )
fin >> v[i];
heavy.init ( n );
for ( int i = 1; i < n; i++ ) {
fin >> x >> y;
heavy.add_edge ( x, y );
}
heavy.build ();
for ( int i = 1; i <= n; i++ )
heavy.update ( i, v[i] );
for ( int i = 1; i <= q; i++ ) {
fin >> t >> x >> y;
if ( t == 0 )
heavy.update ( x, y );
else
fout << heavy.query ( x, y ) << '\n';
}
return 0;
}