#include <iostream>
#include <fstream>
#include <vector>
using namespace std;
class SegmentTree
{
public:
SegmentTree( const int n, vector <int> &v )
{
N = n;
T = vector <int>( 4 * N );
build( 1, 0, N - 1, v );
}
void update( int pos, int val )
{
update( 1, 0, N - 1, pos, val );
}
int query( int st, int dr )
{
return query( 1, 0, N - 1, st, dr );
}
private:
vector <int> T;
int N;
void build( int nod, int st, int dr, vector <int> &v )
{
if ( st == dr )
{
T[nod] = v[st];
}
else
{
int m = ( st + dr ) / 2;
build( 2 * nod, st, m, v );
build( 2 * nod + 1, m + 1, dr, v );
T[nod] = max( T[2 * nod], T[2 * nod + 1] );
}
}
void update( int nod, int st, int dr, int pos, int val )
{
if ( st == dr )
{
T[nod] = val;
}
else
{
int m = ( st + dr ) / 2;
if ( pos <= m ) update( 2 * nod, st, m, pos, val );
else update( 2 * nod + 1, m + 1, dr, pos, val );
T[nod] = max( T[2 * nod], T[2 * nod + 1] );
}
}
int query( int nod, int st, int dr, int st_q, int dr_q )
{
if ( st_q <= st && dr <= dr_q )
{
return T[nod];
}
else
{
int m = ( st + dr ) / 2;
int sol = 0;
if ( st_q <= m ) sol = max( sol, query( 2 * nod, st, m, st_q, dr_q ) );
if ( m < dr_q ) sol = max( sol, query( 2 * nod + 1, m + 1, dr, st_q, dr_q ) );
return sol;
}
}
};
const int Nmax = 100000 + 2;
vector <int> Graph[Nmax];
int value[Nmax];
int size[Nmax], father[Nmax], level[Nmax], path[Nmax], length_path[Nmax];
int pos_in_path[Nmax], start_node[Nmax];
vector <SegmentTree> SegmentTrees;
int Number_Of_Paths;
int N, Q;
void DFS( int nod )
{
int heaviestSon = 0;
size[nod] = 1;
for ( auto son: Graph[nod] )
if ( father[son] == 0 )
{
father[son] = nod;
level[son] = level[nod] + 1;
DFS( son );
size[nod] += size[son];
if ( size[heaviestSon] < size[son] )
heaviestSon = son;
}
if ( heaviestSon == 0 )
path[nod] = Number_Of_Paths++;
else
path[nod] = path[heaviestSon];
pos_in_path[nod] = length_path[ path[nod] ]++;
}
void build_heavy_path_decomposition()
{
father[1] = 1;
level[1] = 0;
DFS( 1 );
for ( int i = 1; i <= N; ++i )
{
pos_in_path[i] = length_path[ path[i] ] - pos_in_path[i] - 1;
if ( pos_in_path[i] == 0 )
start_node[ path[i] ] = i;
}
}
void build_segment_trees()
{
vector < vector<int> > pathValue = vector < vector<int> >( Number_Of_Paths );
for ( int i = 0; i < Number_Of_Paths; ++i )
pathValue[i] = vector<int>( length_path[i] );
for ( int i = 1; i <= N; ++i )
pathValue[ path[i] ][ pos_in_path[i] ] = value[i];
for ( int i = 0; i < Number_Of_Paths; ++i )
SegmentTrees.push_back( SegmentTree( pathValue[i].size(), pathValue[i] ) );
}
int query( int x, int y )
{
if ( level[ start_node[ path[x] ] ] < level[ start_node[ path[y] ] ] )
swap( x, y );
if ( path[x] == path[y] )
return SegmentTrees[ path[x] ].query( min( pos_in_path[x], pos_in_path[y] ), max( pos_in_path[x], pos_in_path[y] ) );
else
return max( SegmentTrees[ path[x] ].query( 0, pos_in_path[x] ), query( father[ start_node[ path[x] ] ], y ) );
}
int main()
{
ifstream f("heavypath.in");
ofstream g("heavypath.out");
f >> N >> Q;
for ( int i = 1; i <= N; ++i )
f >> value[i];
for ( int i = 1, a, b; i < N; ++i )
{
f >> a >> b;
Graph[a].push_back( b );
Graph[b].push_back( a );
}
build_heavy_path_decomposition();
build_segment_trees();
for ( int i = 1, tip, a, b; i <= Q; ++i )
{
f >> tip >> a >> b;
if ( tip == 0 )
{
value[a] = b;
SegmentTrees[ path[a] ].update( pos_in_path[a], b );
}
else
{
g << query( a, b ) << "\n";
}
}
return 0;
}