#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
using namespace std;
ifstream fin("heavypath.in");
ofstream fout("heavypath.out");
struct AINT {
vector<int>aint;
void resizeAint(int n) {
int log_sup = (int)log2(n);
if ((1 << log_sup) < n)
{
log_sup++;
}
aint.resize(1 << (1 + log_sup), 0);
}
void updateAint(int pozTree, int st, int dr, int pozUpdate, int valUpdate) {
if (st == dr) {
aint[pozTree] = valUpdate;
return;
}
int leftChild = 2 * pozTree;
int rightChild = 2 * pozTree + 1;
int mij = (st + dr) / 2;
if (pozUpdate <= mij) {
updateAint(leftChild, st, mij, pozUpdate, valUpdate);
}
else {
updateAint(rightChild, mij + 1, dr, pozUpdate, valUpdate);
}
aint[pozTree] = max(aint[leftChild], aint[rightChild]);
}
int queryAint(int pozTree, int st, int dr, int stFind, int drFind) {
if (st >= stFind && dr <= drFind) {
return aint[pozTree];
}
int leftChild = 2 * pozTree;
int rightChild = 2 * pozTree + 1;
int mij = (st + dr) / 2;
int rightAnswr = 0;
int leftAnswr = 0;
if (stFind <= mij) {
leftAnswr = queryAint(leftChild, st, mij, stFind, drFind);
}
if (drFind > mij) {
rightAnswr = queryAint(rightChild, mij + 1, dr, stFind, drFind);
}
return max(leftAnswr, rightAnswr);
}
};
struct lant {
int depthStart;
int depthEnd;
int rootNode;
lant() :depthStart(0), depthEnd(0), rootNode(0) {};
};
const int NMAX = 1e5+10;
const int MAXLOG = 19;
int anc[NMAX][MAXLOG];
vector<lant>l;
int nrLant = 0;
vector<int>subTreeSize;
vector<int>indexLant;
vector<int>depth;
vector<int>newIdx;
vector<vector<int>>arb;
void DFS(int nodCrt, int father,int crtDepth) {
subTreeSize[nodCrt] = 1;
depth[nodCrt] = crtDepth;
if (arb[nodCrt].size()==1 && father != -1) {
indexLant[nodCrt] = nrLant;
l.push_back(lant());
l.back().depthStart = crtDepth;
l.back().depthEnd = crtDepth;
l.back().rootNode = nodCrt;
nrLant++;
}
int maxSz = 0;
int idxCopil = 1e6+1;//cu ce copil unim
for (auto i : arb[nodCrt]) {
if (i != father) {
DFS(i, nodCrt, crtDepth + 1);
subTreeSize[nodCrt] += subTreeSize[i];
if (subTreeSize[i] > maxSz || (subTreeSize[i] == maxSz && i>idxCopil)) {
maxSz = subTreeSize[i];
idxCopil = i;
}
}
}
if (idxCopil != 1e6+1) {
indexLant[nodCrt] = indexLant[idxCopil];
l[indexLant[idxCopil]].rootNode = nodCrt;
l[indexLant[idxCopil]].depthStart = crtDepth;
}
}
int cnt = 1;
void reorderNodes(int nodCrt,int father) {
newIdx[nodCrt] = cnt;
++cnt;
vector<pair<int, int>>maxSize;
for (auto i : arb[nodCrt]) {
if (i != father) {
maxSize.push_back(make_pair(subTreeSize[i], i));
}
}
sort(maxSize.begin(), maxSize.end(),greater<pair<int,int>>());
for (int i = 0; i < maxSize.size(); ++i) {
reorderNodes(maxSize[i].second, nodCrt);
}
}
AINT maxNode;//maxNode pe fiecare lant
int n;
int lift(int node,int depthCrt) {
int maxi = 0;
while (l[indexLant[node]].depthStart > depthCrt) {
int root = l[indexLant[node]].rootNode;
maxi = max(maxi, maxNode.queryAint(1, 1, n, newIdx[root], newIdx[node]));
node = anc[root][0];
}
int lcaIdx = newIdx[node] - (depth[node] - depthCrt);
maxi = max(maxi, maxNode.queryAint(1, 1, n, lcaIdx, newIdx[node]));
return maxi;
}
void precalcAnc(int nodCrt, int father) {
anc[nodCrt][0] = father;
for (int i = 1; i < MAXLOG; ++i) {
int up = anc[nodCrt][i - 1];
anc[nodCrt][i] = anc[up][i - 1];
}
for (auto i:arb[nodCrt]) {
if (i != father) {
precalcAnc(i, nodCrt);
}
}
}
int cb(int u, int v) {
for (int i = MAXLOG - 1; i >= 0; i--) {
if (anc[u][i] != anc[v][i]) {
u = anc[u][i];
v = anc[v][i];
}
}
return depth[anc[u][0]];
}
int getDepthLca(int u, int v) {
if (depth[u] < depth[v]) {
swap(u, v);
}
for (int i = MAXLOG - 1; i >= 0; i--) {
if (depth[u]-(1<<i) >= depth[v]) {
u = anc[u][i];
}
}
if (u == v) {
return depth[v];
}
return cb(u, v);
}
int main()
{
int q;
fin >> n>>q;
vector<int>v(n+1);
for (int i = 1; i <= n; ++i) {
fin >> v[i];
}
arb.resize(n + 1);
indexLant.resize(n + 1);
subTreeSize.resize(n + 1);
depth.resize(n + 1);
newIdx.resize(n + 1);
int nod1,nod2;
for (int i = 0; i < n - 1; ++i) {
fin >> nod1 >> nod2;
arb[nod1].push_back(nod2);
arb[nod2].push_back(nod1);
}
DFS(1, -1, 0);
reorderNodes(1, -1);
precalcAnc(1, 0);
maxNode.resizeAint(n + 2);
for (int i = 1; i <= n; ++i) {
maxNode.updateAint(1, 1, n, newIdx[i], v[i]);
}
int type, x, y;
for (int i = 0; i < q; ++i) {
fin >> type >> x >> y;
if (type == 0) {
maxNode.updateAint(1, 1, n, newIdx[x], y);
}else{
int depthLCA = getDepthLca(x, y);
fout << max(lift(x, depthLCA), lift(y, depthLCA))<<"\n";
}
}
return 0;
}
//=^..^=