#include <bits/stdc++.h>
using namespace std;
const int Nmax = 100000 + 1;
int Merge(int a, int b)
{
return max(a, b);
}
class SegmentTree
{
public:
SegmentTree (const vector<int>& v)
{
N = static_cast<int>( v.size() );
A = vector<int>(4 * N, 0);
build(1, 0, N - 1, v);
}
void update(int pos, int new_val)
{
update(1, 0, N - 1, pos, new_val);
}
int query(int x, int y)
{
return query(1, 0, N - 1, x, y);
}
void build(int nod, int st, int dr, const vector<int>& v)
{
if (st == dr)
A[nod] = v[st];
else
{
int m = (st + dr) / 2;
build(2 * nod, st, m, v);
build(2 * nod + 1, m + 1, dr, v);
A[nod] = Merge(A[2 * nod], A[2 * nod + 1]);
}
}
void update(int nod, int st, int dr, int pos, int value)
{
if (st == dr)
A[nod] = value;
else
{
int m = (st + dr) / 2;
if ( pos <= m )
update(2 * nod, st, m, pos, value);
else
update(2 * nod + 1, m + 1, dr, pos, value);
A[nod] = Merge(A[2 * nod], A[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 A[nod];
else
{
int m = (st + dr) / 2;
int sol = numeric_limits<int>::min();
if ( st_q <= m )
sol = Merge(sol, query(2 * nod, st, m, st_q, dr_q));
if ( m < dr_q )
sol = Merge(sol, query(2 * nod + 1, m + 1, dr, st_q, dr_q));
return sol;
}
}
private:
int N;
vector<int> A;
};
vector<SegmentTree> ST;
vector<int> G[Nmax];
int father[Nmax], size[Nmax], depth[Nmax], value[Nmax], path[Nmax];
int pos_in_path[Nmax], length_path[Nmax], start_node[Nmax];
int N, Q, NrPaths;
void dfs(int nod)
{
int hson = 0;
size[nod] = 1;
for(int son: G[nod])
{
if ( father[son] == 0 )
{
father[son] = nod;
depth[son] = depth[nod] + 1;
dfs(son);
if ( size[hson] < size[son] )
hson = son;
}
}
if (hson == 0)
path[nod] = NrPaths++;
else
path[nod] = path[hson];
pos_in_path[nod] = length_path[ path[nod] ]++;
}
void build_heavy()
{
father[1] = 1;
depth[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> > pathValues(NrPaths);
for ( int i = 0; i < NrPaths; ++i )
pathValues[i] = vector<int>(length_path[i]);
for ( int i = 1; i <= N; ++i )
pathValues[ path[i] ][ pos_in_path[i] ] = value[i];
for ( int i = 0; i < NrPaths; ++i )
ST.push_back(SegmentTree(pathValues[i]));
}
void update(int nod, int new_val)
{
value[nod] = new_val;
ST[ path[nod] ].update(pos_in_path[nod], new_val);
}
int query(int x, int y)
{
if ( depth[ start_node[ path[x] ] ] < depth[ start_node[ path[y] ] ] )
swap(x, y);
if ( path[x] == path[y] )
return ST[ path[x] ].query(min(pos_in_path[x], pos_in_path[y]), max(pos_in_path[x], pos_in_path[y]));
else
return Merge(ST[ path[x] ].query(0, pos_in_path[x]), query(father[ start_node[ path[x] ] ], y));
}
int main()
{
ifstream in("heavypath.in");
ofstream out("heavypath.out");
in.sync_with_stdio( false );
in >> N >> Q;
for ( int i = 1; i <= N; ++i )
in >> value[i];
for ( int i = 1, a, b; i < N; ++i )
{
in >> a >> b;
G[a].push_back( b );
G[b].push_back( a );
}
build_heavy();
build_segment_trees();
for ( int i = 1, tip, a, b; i <= Q; ++i )
{
in >> tip >> a >> b;
if ( tip == 0 )
{
update( a, b );
}
else
{
out << query( a, b ) << "\n";
}
}
return 0;
}