#define MAX_N 100000
#define MAX_N_LOG 16
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <memory>
#include <utility>
using namespace std;
ifstream fin("heavypath.in");
ofstream fout("heavypath.out");
struct ITNode
{
ITNode(int _a, int _b, ITNode* _parent) : a(_a), b(_b), parent(_parent), left(nullptr), right(nullptr)
{
}
int a, b, ma;
ITNode *parent, *left, *right;
};
// Interval tree
struct IT
{
IT(vector<int>&& _inter) : inter(move(_inter)), leaves(inter.size())
{
ConstructIT(0, inter.size() - 1, root = new ITNode(0, inter.size() - 1, nullptr));
}
~IT()
{
Cleanup(root);
}
void ChangeVal(int index, int val)
{
--index;
inter[index] = val;
leaves[index]->ma = val;
Update(leaves[index]->parent);
}
int GetMax(int a, int b)
{
{
--a;
--b;
}
if (a > b)
{
swap(a, b);
}
return GetRange(leaves[a], a, b);
}
int GetRange(ITNode* node, int a, int b)
{
if (node->b < b)
{
int ma = 0;
if (node->right != nullptr)
{
if ((a <= node->right->a) && (b >= node->right->b))
{
ma = node->right->ma;
}
}
return max(ma, GetRange(node->parent, a, b));
}
else
{
if (node->b == b)
{
if (node->a >= a)
{
return node->ma;
}
else
{
return GetRange(node->right, a, b);
}
}
if (node->left->b < b)
{
int lma = 0;
if (node->left->a >= a)
{
lma = node->left->ma;
}
return max(lma, GetRange(node->right, a, b));
}
else
{
return GetRange(node->left, a, b);
}
}
}
void Update(ITNode* node)
{
if (node != nullptr)
{
int lma = 0, rma = 0;
if (node->left != nullptr)
{
lma = node->left->ma;
}
if (node->right != nullptr)
{
rma = node->right->ma;
}
node->ma = max(lma, rma);
Update(node->parent);
}
}
void Cleanup(ITNode*& node)
{
if (node != nullptr)
{
Cleanup(node->left);
Cleanup(node->right);
delete node;
node = nullptr;
}
}
void ConstructIT(int a, int b, ITNode* node)
{
node->a = a;
node->b = b;
if (a == b)
{
node->ma = inter[a];
leaves[a] = node;
}
else
{
const int mid = (a + b) / 2;
node->left = new ITNode(a, mid, node);
node->right = new ITNode(mid + 1, b, node);
ConstructIT(a, mid, node->left);
ConstructIT(mid + 1, b, node->right);
node->ma = max(node->left->ma, node->right->ma);
}
}
ITNode* root = nullptr;
vector<int> inter;
vector<ITNode*> leaves;
};
struct A
{
~A()
{
if (tree != nullptr)
{
delete tree;
tree = nullptr;
}
}
IT* tree = nullptr;
int bottom, top;
};
int n, m;
int T[MAX_N + 1], V[MAX_N + 1], LVL[MAX_N + 1];
shared_ptr<A> Piece[MAX_N + 1];
shared_ptr<vector<int>> Paths[MAX_N + 1];
vector<int> G[MAX_N + 1];
int glEInd = 0;
int E[2 * MAX_N], P[MAX_N + 1], RMQ[MAX_N_LOG + 2][2 * MAX_N];
void ReadData();
void Init();
void EulerTour(int node);
void DoRmq();
int LCA(int x, int y);
int PathDecomposition(int node);
int MaxValueOnRoad(int x, int y);
void ChangeNodeValue(int node, int value);
int main()
{
ReadData();
Init();
for (int i = 1; i <= m; ++i)
{
int t, x, y;
fin >> t >> x >> y;
if (t == 0)
{
ChangeNodeValue(x, y);
}
else
{
fout << MaxValueOnRoad(x, y) << '\n';
}
}
fin.close();
fout.close();
return 0;
}
void Dfs(int node)
{
for (int adjNode : G[node])
{
if (T[node] != adjNode)
{
T[adjNode] = node;
Dfs(adjNode);
}
}
}
void ReadData()
{
fin >> n >> m;
for (int i = 1; i <= n; ++i)
{
fin >> V[i];
}
for (int i = 1; i < n; ++i)
{
int x, y;
fin >> x >> y;
G[x].push_back(y);
G[y].push_back(x);
}
}
void Init()
{
for (int i = 1; i <= n; ++i)
{
T[i] = 0;
}
Dfs(1);
LVL[1] = 1;
EulerTour(1);
DoRmq();
PathDecomposition(1);
for (int i = 1; i <= n; ++i)
{
if (Piece[i].get() == nullptr)
{
Piece[i] = shared_ptr<A>(new A);
for (int node : (*Paths[i]))
{
Piece[node] = Piece[i];
}
Piece[i]->bottom = (*Paths[i]).front();
Piece[i]->top = (*Paths[i]).back();
vector<int> values;
for (int node : (*Paths[i]))
{
values.push_back(V[node]);
}
Piece[i]->tree = new IT(move(values));
}
}
}
void EulerTour(int node)
{
E[++glEInd] = node;
P[node] = glEInd;
for (int adjNode : G[node])
{
if (T[adjNode] == node)
{
LVL[adjNode] = LVL[node] + 1;
EulerTour(adjNode);
E[++glEInd] = node;
}
}
}
void DoRmq()
{
for (int i = 1; i < (2 * n); ++i)
{
RMQ[0][i] = LVL[E[i]];
}
for (int exp = 1; (1 << exp) < (2 * n); ++exp)
{
for (int i = 1; (i + (1 << (exp - 1))) < (2 * n); ++i)
{
RMQ[exp][i] = min(RMQ[exp - 1][i], RMQ[exp - 1][i + (1 << (exp - 1))]);
}
}
}
int LCA(int x, int y)
{
x = P[x];
y = P[y];
if (x > y)
{
swap(x, y);
}
const int l = y - x + 1;
int logL = -1;
{
int auxL = l;
while (auxL != 0)
{
auxL /= 2;
++logL;
}
}
return min(RMQ[logL][x], RMQ[logL][y - (1 << logL) + 1]);
}
int PathDecomposition(int node)
{
int res = 1;
int resAdjMax = 0;
int adjNodeMax = -1;
for (int adjNode : G[node])
{
if (T[adjNode] == node)
{
int resAdj = PathDecomposition(adjNode);
if (resAdjMax < resAdj)
{
resAdjMax = resAdj;
adjNodeMax = adjNode;
}
res += resAdj;
}
}
if (adjNodeMax == -1)
{
Paths[node] = shared_ptr<vector<int>>(new vector<int>());
}
else
{
Paths[node] = Paths[adjNodeMax];
}
Paths[node]->push_back(node);
return res;
}
int GetMaxValueOnParentRoad(int x, int y)
{
if (Piece[x].get() == Piece[y].get())
{
int posX = LVL[Piece[x]->bottom] - LVL[x] + 1;
int posY = LVL[Piece[x]->bottom] - LVL[y] + 1;
return Piece[x]->tree->GetMax(posX, posY);
}
else
{
return max(Piece[x]->tree->GetMax(LVL[Piece[x]->bottom] - LVL[x] + 1, LVL[Piece[x]->bottom] - LVL[Piece[x]->top] + 1), GetMaxValueOnParentRoad(T[Piece[x]->top], y));
}
}
int MaxValueOnRoad(int x, int y)
{
int lca = LCA(x, y);
int res1 = GetMaxValueOnParentRoad(x, lca);
int res2 = GetMaxValueOnParentRoad(y, lca);
return max(res1, res2);
}
void ChangeNodeValue(int node, int value)
{
int nodeInd = LVL[Piece[node]->bottom] - LVL[node] + 1;
V[node] = value;
Piece[node]->tree->ChangeVal(nodeInd, value);
}