#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <cassert>
#include <cstdio>
using namespace std;
const int nMax = 100005;
const int logMax = 20;
const int INF = (1 << 30);
class arbint
{
public:
arbint(int sz)
{
n = sz;
int lg = ceil(log2(n));
arb = new int[1 << lg + 1];
}
inline void Update(int ind, int val)
{
Update(ind, val, 1, 1, n);
}
inline int MaxQuery(int start, int finish)
{
return MaxQuery(start, finish, 1, n, 1);
}
private:
int *arb;
int n;
void Update(int ind, int val, int nod, int st, int dr)
{
if(st == dr)
{
arb[nod] = val;
return;
}
int mid = (st + dr) / 2;
if(ind <= mid)
Update(ind, val, nod * 2, st, mid);
else
Update(ind, val, nod * 2 + 1, mid + 1, dr);
arb[nod] = max(arb[nod*2], arb[nod*2+1]);
}
int MaxQuery(int start, int finish, int st, int dr, int nod = 1)
{
if(start <= st && finish >= dr)
return arb[nod];
int mid = (st + dr) / 2;
int ret = -INF;
if(start <= mid)
ret = max(ret, MaxQuery(start, finish, st, mid, nod * 2));
if(finish > mid)
ret = max(ret, MaxQuery(start, finish, mid + 1, dr, nod * 2 + 1));
return ret;
}
};
struct lant
{
lant()
{
noduri.push_back(0);
}
~lant()
{
}
vector<int> noduri;
arbint *aint;
int lastNod;
int sz;
void UpdateAll()
{
sz = noduri.size();
aint = new arbint(sz - 1);
for(int i = 1; i < sz; ++i)
aint->Update(i, noduri[i]);
vector<int>().swap(noduri);
}
inline void Update(int ind, int val)
{
aint->Update(ind, val);
}
inline int MaxQuery(int start, int finish)
{
return aint->MaxQuery(start, finish);
}
};
class arbore
{
public:
arbore(int n)
{
this->n = n;
vecini.resize(n + 1);
val.resize(n + 1);
lantNr.resize(n + 1);
lantPos.resize(n + 1);
heavy.resize(n + 1);
nivel.resize(n + 1);
for(int i = 0; i < logMax; ++i)
father[i].resize(n + 1);
}
void AddEdge(int x, int y)
{
vecini[x].push_back(y);
vecini[y].push_back(x);
}
void BuildPath()
{
DFS(1, 1);
lanturi[lantNr[1]].UpdateAll();
for(int i = 1; (1 << i) <= n; ++i)
for(int j = 1; j <= n; ++j)
father[i][j] = father[i-1][father[i-1][j]];
}
void Update(int ind, int value)
{
val[ind] = value;
lanturi[lantNr[ind]].Update(lantPos[ind], value);
}
int Query(int x, int y)
{
int lca = GetLCA(x, y);
if(lca == x)
return QueryLCA(x, y);
if(lca == y)
return QueryLCA(y, x);
return max(QueryLCA(lca, x), QueryLCA(lca, y));
}
vector<int> val;
private:
int n;
vector<vector<int> > vecini;
vector<int> lantNr;
vector<int> lantPos;
vector<lant> lanturi;
vector<int> heavy;
vector<int> father[logMax];
vector<int> nivel;
int QueryLCA(int lca, int y)
{
int x = lca;
int ret = val[x];
while(x != y)
{
if(lantNr[x] == lantNr[y])
{
ret = max(ret, lanturi[lantNr[x]].MaxQuery(lantPos[y], lantPos[x]));
y = x;
}
else
{
ret = max(ret, lanturi[lantNr[y]].MaxQuery(lantPos[y], lanturi[lantNr[y]].sz - 1));
y = father[0][lanturi[lantNr[y]].lastNod];
}
}
return ret;
}
void DFS(int current, int fth)
{
father[0][current] = fth;
nivel[current] = nivel[fth] + 1;
if(current != 1 && vecini[current].size() == 1)
{
lant x;
x.noduri.push_back(val[current]);
x.lastNod = current;
lanturi.push_back(x);
lantNr[current] = lanturi.size() - 1;
lantPos[current] = 1;
heavy[current] = 1;
return;
}
for(auto v:vecini[current])
if(v != fth)
DFS(v, current);
int maxHeavy = 0, ind;
for(auto v:vecini[current])
if(v != fth)
{
heavy[current] += heavy[v];
if(heavy[v] > maxHeavy)
{
maxHeavy = heavy[v];
ind = v;
}
}
for(auto v:vecini[current])
if(v != fth && v != ind)
lanturi[lantNr[v]].UpdateAll();
lantNr[current] = lantNr[ind];
lanturi[lantNr[current]].noduri.push_back(val[current]);
lanturi[lantNr[current]].lastNod = current;
lantPos[current] = lantPos[ind] + 1;
}
int GetLCA(int x, int y)
{
if(nivel[x] < nivel[y])
swap(x, y);
int step;
for(step = 0; (1 << step) < n; ++step);
for(int i = step; i >= 0; --i)
if(nivel[father[i][x]] >= nivel[y])
x = father[i][x];
if(x == y)
return x;
for(int i = step; i >= 0; --i)
if(father[i][x] != father[i][y])
x = father[i][x], y = father[i][y];
return father[0][x];
}
};
int main()
{
int n, m;
ifstream in("heavypath.in");
ofstream out("heavypath.out");
in >> n >> m;
arbore arb(n);
for(int i = 1; i <= n; ++i)
in >> arb.val[i];
int a, b;
for(int i = 1; i < n; ++i)
{
in >> a >> b;
arb.AddEdge(a, b);
}
arb.BuildPath();
int t, x, y;
while(m--)
{
in >> t >> x >> y;
if(t == 0)
arb.Update(x, y);
else
out << arb.Query(x, y) << "\n";
}
in.close();
out.close();
return 0;
}