#include <bits/stdc++.h>
using namespace std;
const int NMAX = 1e5;
int val[NMAX+2];
namespace HLD
{
/// one time use only, reset by hand if necessary
const int NMAX = ::NMAX;
const int INF = (1 << 30);
struct Atom
{
int cnt, val;
Atom(int cnt = 0, int val = -INF) : cnt(cnt), val(val) {}
};
Atom combine(const Atom& a, const Atom& b)
{
return Atom(a.cnt + b.cnt, max(a.val, b.val));
}
int root[NMAX+2], treePos[NMAX+2], unde[NMAX+2];
int parent[NMAX+2], heavy[NMAX+2], depth[NMAX+2];
vector<Atom> aint[NMAX+2];
int _nr, _lg[NMAX+2];
void upd_aint(int care, int nod, int st, int dr, int poz, int val)
{
if( poz < st || poz > dr ) return ;
if( st == dr ) {
aint[care][nod] = Atom(1, val);
return ;
}
int m = (st + dr) / 2;
upd_aint(care, 2*nod, st, m, poz, val);
upd_aint(care, 2*nod + 1, m+1, dr, poz, val);
aint[care][nod] = combine(aint[care][2*nod], aint[care][2*nod + 1]);
}
int qry_aint(int care, int nod, int st, int dr, int x, int y)
{
if( y < st || dr < x ) return -INF;
if( x <= st && dr <= y ) {
///cerr << "qry: " << care << ": " << st << '-' << dr << ": " << aint[care][nod].val << '\n';
return aint[care][nod].val;
}
int m = (st + dr) / 2;
int a = qry_aint(care, 2*nod, st, m, x, y);
int b = qry_aint(care, 2*nod + 1, m+1, dr, x, y);
return max(a, b);
}
int query(int x, int y)
{
int ret = -INF;
while( root[x] != root[y] ) {
if( depth[x] < depth[y] ) swap(x, y);
ret = max(ret, qry_aint(unde[x], 1, 1,_lg[ unde[x] ], 1, treePos[x]));
x = parent[ root[x] ];
}
if( depth[x] > depth[y] ) swap(x, y);
ret = max(ret, qry_aint(unde[x], 1, 1,_lg[ unde[x] ], treePos[x], treePos[y]));
return ret;
}
void update(int nod, int val)
{
///cerr << "aint: " << unde[nod] << ", lg: " << _lg[ unde[nod] ] << ", poz: " << treePos[nod] << ", val: " << val << '\n';
upd_aint(unde[nod], 1, 1,_lg[ unde[nod] ], treePos[nod], val);
}
int dfs(const vector<int>(& G)[NMAX+2], int nod, int father = -1)
{
if( father == -1 ) depth[nod] = 1;
int cat = 1;
int greu = 0;
for( int x : G[nod] ) {
if( x == father ) continue;
parent[x] = nod;
depth[x] = depth[nod] + 1;
int subTree = dfs(G, x, nod);
if( subTree > greu )
heavy[nod] = x, greu = subTree;
cat += subTree;
}
return cat;
}
void init(const vector<int>(& G)[NMAX+2], int noduri)
{
parent[0] = -1;
parent[1] = 0;
dfs(G, 1);
for( int i = 1; i <= noduri; ++i ) {
if( parent[i] == 0 || heavy[ parent[i] ] != i ) {
++_nr;
for( int x = i, cnt = 1; 1 <= x && x <= noduri; x = heavy[x], ++cnt )
treePos[x] = cnt, _lg[_nr] = cnt, root[x] = i, unde[x] = _nr;
aint[_nr] = vector<Atom>(4 * _lg[_nr] + 2);
for( int x = i, cnt = 1; 1 <= x && x <= noduri; x = heavy[x], ++cnt )
update(x, ::val[x]);
}
}
}
}
ifstream in("heavypath.in");
ofstream out("heavypath.out");
vector<int> G[NMAX+2];
int N, Q;
int main()
{
in >> N >> Q;
for( int i = 1; i <= N; ++i ) in >> val[i];
for( int i = 1; i < N; ++i ) {
int x,y;
in >> x >> y;
G[x].push_back(y);
G[y].push_back(x);
}
HLD::dfs(G, 1);
HLD::init(G, N);
for( int i = 1; i <= N; ++i )
HLD::update(i, val[i]);
for( int i = 1; i <= Q; ++i ) {
int t,x,y;
in >> t >> x >> y;
if( t == 0 ) {
HLD::update(x, y);
}
else
out << HLD::query(x, y) << '\n';
}
return 0;
}