#include <fstream>
#include <vector>
using namespace std;
///dfs normal pt calcularea de dad, weight, level
///dfsHeavy pt calcularea lanturilor grele:
///1) calculam heavyHead[node] = capatul lantului greu in care se afla nodul node
///2) calculam dfsOrder[node] = pozitia nodului node in sirul dfsGreu
///AINT pentru update/query
///1) la update, adaugam V la dfsOrder[node]
///2) la query, gasim lca-ul celor 2 noduri urcand in sus pe lanturile grele
ifstream cin("heavypath.in");
ofstream cout("heavypath.out");
const int NMAX = 100000;
///AINT///
struct arbint
{
int v[4 * NMAX];
void Update(int node, int l, int r, int pos, int val)
{
if(l == r)
{
v[node] = val;
return;
}
int mid = (l + r) >> 1;
if(pos <= mid)
Update(2 * node, l, mid, pos, val);
else
Update(2 * node + 1, mid + 1, r, pos, val);
v[node] = max(v[2 * node], v[2 * node + 1]);
}
int Query(int node, int l, int r, int st, int dr)
{
if(st <= l && r <= dr)
return v[node];
int mid = (l + r) >> 1;
if(dr <= mid)
return Query(2 * node, l, mid, st, dr);
else if(st >= mid + 1)
return Query(2 * node + 1, mid + 1, r, st, dr);
else
return max(Query(2 * node, l, mid, st, mid),
Query(2 * node + 1, mid + 1, r, mid + 1, dr));
}
};
arbint aint;
///AINT///
int N, M, vals[NMAX + 5];
vector <int> g[NMAX + 5], order;
int dad[NMAX + 5], weight[NMAX + 5], level[NMAX + 5];
int heavyHead[NMAX + 5], dfsOrder[NMAX + 5];
void dfs(int node, int _dad)
{
dad[node] = _dad;
level[node] = level[_dad] + 1;
weight[node] = 1;
for(auto it : g[node])
if(it != _dad)
{
dfs(it, node);
weight[node] += weight[it];
}
}
void dfsHeavy(int node)
{
dfsOrder[node] = (int)order.size() + 1;
order.push_back(node);
if(g[node].size())
{
int heavySon = g[node][0];
if(heavySon == dad[node])
{
if(g[node].size() >= 2)
heavySon = g[node][1];
else
return ;
}
for(auto it : g[node])
if(it != dad[node] && weight[it] > weight[heavySon])
heavySon = it;
heavyHead[heavySon] = heavyHead[node];
dfsHeavy(heavySon);
for(auto it : g[node])
if(it != dad[node] && it != heavySon)
dfsHeavy(it);
}
}
int QueryLca(int x, int y)
{
///x si y sunt pe acelasi lant greu
if(heavyHead[x] == heavyHead[y])
{
if(dfsOrder[x] > dfsOrder[y])
swap(x, y);
return aint.Query(1, 1, N, dfsOrder[x], dfsOrder[y]);
}
if(level[dad[heavyHead[x]]] > level[dad[heavyHead[y]]])
{
int st = min(dfsOrder[x], dfsOrder[heavyHead[x]]),
dr = max(dfsOrder[x], dfsOrder[heavyHead[x]]);
return max(aint.Query(1, 1, N, st, dr),
QueryLca(dad[heavyHead[x]], y));
}
int st = min(dfsOrder[y], dfsOrder[heavyHead[y]]),
dr = max(dfsOrder[y], dfsOrder[heavyHead[y]]);
return max(aint.Query(1, 1, N, st, dr),
QueryLca(x, dad[heavyHead[y]]));
}
int main()
{
cin >> N >> M;
for(int i = 1; i <= N; i++)
cin >> vals[i];
for(int i = 1; i < N; i++)
{
int x, y;
cin >> x >> y;
g[x].push_back(y);
g[y].push_back(x);
}
dfs(1, 0);
for(int i = 1; i <= N; i++)
heavyHead[i] = i;
dfsHeavy(1);
for(int i = 1; i <= N; i++)
aint.Update(1, 1, N, dfsOrder[i], vals[i]);
for(int i = 1; i <= M; i++)
{
int c, x, y;
cin >> c >> x >> y;
if(c == 0)
aint.Update(1, 1, N, dfsOrder[x], y);
else
cout << QueryLca(x, y) << '\n';
}
return 0;
}