#include <bits/stdc++.h>
#define ll long long
#define fi first
#define se second
using namespace std;
ifstream in("heavypath.in");
ofstream out("heavypath.out");
const int N = 1e5 + 5;
int n, m, chainsNr;
vector<int> v[N];
int depth[N], dim[N], chainId[N];
vector<int> chains[N];
vector<int> aint[N];
int dad[N];
int values[N];
void dfs(int node, int parent = 0) {
dad[node] = parent;
depth[node] = depth[parent] + 1;
dim[node] = 1;
int bestSon = 0;
for (auto son : v[node]) {
if (!depth[son]) {
dfs(son, node);
dim[node] += dim[son];
if (dim[son] > dim[bestSon]) {
bestSon = son;
}
}
}
if (bestSon == 0) {
// pentru o frunza cream un lant nou
chainId[node] = ++chainsNr;
} else {
// altfel unim nodul cu lantul asociat fiului cu dimensiunea subarborelui maxima
chainId[node] = chainId[bestSon];
}
chains[chainId[node]].push_back(node);
}
// pozitia unui nod in lantul sau
int getPosition(int node) {
return depth[node] - depth[chains[chainId[node]][0]] + 1;
}
// adancimea primului nod din lantul corespunzator unui nod
int chainDepth(int node) {
return depth[chains[chainId[node]][0]];
}
void update(vector<int> &aint, int st, int dr, int node, int id, int val) {
if (st == dr) {
aint[node] = val;
return;
}
int mid = (st + dr) / 2;
if (id <= mid) {
update(aint, st, mid, 2 * node, id, val);
} else {
update(aint, mid + 1, dr, 2 * node + 1, id , val);
}
aint[node] = max(aint[2 * node], aint[2 * node + 1]);
}
int query(vector<int> &aint, int st, int dr, int node, int l, int r) {
if (l <= st && dr <= r) {
return aint[node];
}
int mid = (st + dr) / 2;
int left = 0, right = 0;
if (l <= mid) {
left = query(aint, st, mid, 2 * node, l, r);
}
if (r > mid) {
right = query(aint, mid + 1, dr, 2 * node + 1, l, r);
}
return max(left, right);
}
void updateChain(int target, int value) {
values[target] = value;
update(aint[chainId[target]], 1, chains[chainId[target]].size(), 1, getPosition(target), values[target]);
}
// primul nod dintr-un lant corespunzator lui node
int firstOnChain(int node) {
return chains[chainId[node]][0];
}
// x si y sunt pe acelasi lant
int maxOnChain(int x, int y) {
int id = chainId[x];
x = getPosition(x);
y = getPosition(y);
if (x > y) {
swap(x, y);
}
return query(aint[id], 1, chains[id].size(), 1, x, y);
}
int getMaxValue(int x, int y) {
int ans = 0;
while (chainId[x] != chainId[y]) {
// urcam pe lantul care se afla mai jos pe arbore
if (chainDepth(x) < chainDepth(y)) {
swap(x, y);
}
ans = max(ans, maxOnChain(firstOnChain(x), x));
// urcam pe lantul urmator
x = dad[firstOnChain(x)];
}
ans = max(ans, maxOnChain(x, y));
return ans;
}
int main() {
in >> n >> m;
for (int i = 1; i <= n; i++) {
in >> values[i];
}
for (int i = 1, x, y; i < n; i++) {
in >> x >> y;
v[x].push_back(y);
v[y].push_back(x);
}
dfs(1);
for (int i = 1; i <= chainsNr; i++) {
// ordonam nodurile in ordine crescatoare adancimii lor
reverse(chains[i].begin(), chains[i].end());
// contruim arborele de intervale pentru lantul curent
aint[i] = vector<int>(4 * (int) chains[i].size() + 1, 0);
for (auto node : chains[i]) {
updateChain(node, values[node]);
}
}
for (int t, x, y; m--;) {
in >> t >> x >> y;
if (t == 0) {
updateChain(x, y);
} else {
out << getMaxValue(x, y) << '\n';
}
}
return 0;
}