#include <fstream>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
ifstream fin( "heavypath.in" );
ofstream fout( "heavypath.out" );
const int MaxN = 100002;
struct TreeNode {
int val, path, pos;
} data[MaxN];
//informatii pentru noduri
int subt[MaxN];
//subt[node] = greutatea arborelui de sub node
vector<int> T[MaxN];
//arborele
vector<int> path[MaxN];
//path[i] = nodurile de pe lantul i
int viz[MaxN];
int dimPath[MaxN], nextNodePath[MaxN];
//dimensiunea lantului
//tatal ultimului nod din lant
//--------------------arborii de intervale-------------------
int segT[5 * MaxN];
//arborii de intervale pentru lanturi
int gapPath[MaxN];
//decalajul pentru fiecare lant in parte
int gap; //(este defapt gapPath[segPath])
//decalajul pentru arborele de intervale coresp. lantului accesat acum din segT
int segPath;
//lantul accesat acum in segT
void build( int node, int st, int dr ) {
int mij = (st + dr) / 2;
if ( st == dr ) {
segT[node + gap] = data[path[segPath][st - 1]].val;
return;
}
build( 2 * node, st, mij );
build( 2 * node + 1, mij + 1, dr );
segT[node + gap] = max( segT[2 * node + gap], segT[2 * node + 1 + gap] );
}
int upos, uval;
//pozitia ce trebuie schimbata din lantul accesat acum
//si valoarea ce trebuie pusa in locul ei
void update( int node, int st, int dr ) {
int mij = (st + dr) / 2;
if ( st == dr ) {
segT[node + gap] = uval;
return;
}
if ( upos <= mij ) {
update( 2 * node, st, mij );
} else {
update( 2 * node + 1, mij + 1, dr );
}
segT[node + gap] = max( segT[2 * node + gap], segT[2 * node + 1 + gap] );
}
int a, b;
//nodurile din segPath pe care se face query
int query( int node, int st, int dr ) {
int mij = (st + dr) / 2, x = 0, y = 0;
if ( a <= st && dr <= b ) {
return segT[node + gap];
}
if ( a <= mij ) {
x = query( 2 * node, st, mij );
}
if ( b > mij ) {
y = query( 2 * node + 1, mij + 1, dr );
}
return max( x, y );
}
//-------------------------impartirea in lanturi (DFS-uri)----------------------
//calculam greutatea nodurilor
int calcSubT( int node ) {
subt[node] = viz[node] = 1;
for ( int i = 0; i < (int)T[node].size(); ++i ) {
if ( !viz[T[node][i]] ) {
subt[node] += calcSubT( T[node][i] );
}
}
return subt[node];
}
int npath;
//lantul curent
void buildPaths( int node ) {
int mxNode = -1, mx = 0, currPath;
viz[node] = 1;
//selectam cel mai greu fiu
for ( int i = 0; i < (int)T[node].size(); ++i ) {
if ( !viz[T[node][i]] ) {
if ( subt[T[node][i]] > mx ) {
mx = subt[T[node][i]];
mxNode = T[node][i];
}
buildPaths( T[node][i] );
}
}
//incheiem lanturile fiilor mai slabi
for ( int i = 0; i < (int)T[node].size(); ++i ) {
if ( T[node][i] != mxNode ) {
nextNodePath[data[T[node][i]].path] = node;
}
}
if ( mxNode != -1 ) { //daca node nu este frunza
//node data
currPath = data[node].path = data[mxNode].path;
data[node].pos = data[mxNode].pos + 1;
//node path data
path[currPath].push_back( node );
++dimPath[currPath];
} else {
//node data
currPath = data[node].path = npath++;
data[node].pos = 1;
//node path data
path[currPath].push_back( node );
++dimPath[currPath];
}
}
//----------------------------codul principal (de folosire a functiilor)-----------------------------
void case0( int &x, int &y ) {
if ( x == 0 ) {
x = 1;
}
if ( y == 0 ) {
y = 1;
}
}
int main() {
int n, m, i, tp, x, y, maxXYpath;
fin >> n >> m;
for ( i = 1; i <= n; ++i ) {
fin >> data[i].val;
}
for ( i = 1; i < n; ++i ) {
fin >> x >> y;
T[x].push_back( y );
T[y].push_back( x );
}
calcSubT( 1 );
for ( i = 1; i <= n; ++i ) {
viz[i] = 0;
}
buildPaths( 1 );
nextNodePath[0] = 0;
//calcularea gapPath
for ( i = 0; i < npath; ++i ) {
gapPath[i] = gapPath[i - 1] + 4 * dimPath[i - 1] + 1; //4 * dimPath[i - 1] = cat este nevoie pentru arborele de intervale al lantului i-1
gap = gapPath[i];
segPath = i;
build( 1, 1, dimPath[i] );
}
//executarea operatiilor
while ( m-- ) {
fin >> tp >> x >> y;
if ( tp == 0 ) { //update
upos = data[x].pos;
uval = y;
gap = gapPath[data[x].path];
segPath = data[x].path;
update( 1, 1, dimPath[data[x].path] );
} else { //query
maxXYpath = 0;
while ( data[x].path != data[y].path ) {
case0( x, y );
//x path query
gap = gapPath[data[x].path];
segPath = data[x].path;
a = data[x].pos;
b = dimPath[data[y].path];
maxXYpath = max( maxXYpath, query( 1, 1, dimPath[data[x].path] ) );
//y path query
gap = gapPath[data[y].path];
segPath = data[y].path;
a = data[y].pos;
b = dimPath[data[y].path];
maxXYpath = max( maxXYpath, query( 1, 1, dimPath[data[y].path] ) );
//trecem la urmatoarele lanturi cu nodurile coresp.
x = nextNodePath[data[x].path];
y = nextNodePath[data[y].path];
}
case0( x, y );
gap = gapPath[data[x].path];
segPath = data[x].path;
a = min( data[x].pos, data[y].pos );
b = max( data[x].pos, data[y].pos );
maxXYpath = max( maxXYpath, query( 1, 1, dimPath[data[x].path] ) );
fout << maxXYpath << "\n";
}
}
fin.close();
fout.close();
return 0;
}