#include <bits/stdc++.h>
#include <climits>
#define MAX_LOG 17
#define MAX_N 100005
using namespace std;
class SegmentTree {
private:
vector<int> tree;
int S;
int E;
int Val;
int Pos;
int N;
void buildInternal(int node, int start, int end, vector<int> &array) {
if(start == end) {
tree[node] = array[start];
return;
}
int middle = (start + end) / 2;
buildInternal(2 * node + 1, start, middle, array);
buildInternal(2 * node + 2, middle + 1, end, array);
tree[node] = max(tree[2 * node + 1], tree[2 * node + 2]);
}
void updateInternal(int node, int start, int end) {
if(start == end) {
tree[node] = Val;
return;
}
int middle = (start + end) / 2;
if(Pos <= middle) {
updateInternal(2 * node + 1, start, middle);
} else {
updateInternal(2 * node + 2, middle + 1, end);
}
tree[node] = max(tree[2 * node + 1], tree[2 * node + 2]);
}
void queryInternal(int node, int start, int end) {
if(S <= start && end <= E) {
Val = max(Val, tree[node]);
return;
}
int middle = (start + end) / 2;
if(S <= middle) {
queryInternal(2 * node + 1, start, middle);
}
if(E > middle) {
queryInternal(2 * node + 2, middle + 1, end);
}
}
public:
SegmentTree(vector<int> array) {
tree = vector<int>(3 * array.size());
N = array.size();
buildInternal(0, 0, N - 1, array);
}
void update(int pos, int val) {
Pos = pos;
Val = val;
updateInternal(0, 0, N - 1);
}
int query(int start, int end) {
S = start;
E = end;
Val = INT_MIN;
queryInternal(0, 0, N - 1);
return Val;
}
};
struct NodeInfo {
int pathIdx;
int position;
int pathParent;
int father;
NodeInfo() {
pathIdx = -1;
position = -1;
pathParent = -1;
father = -1;
}
NodeInfo(int pathIdx_, int position_, int pathParent_, int father_) {
pathIdx = pathIdx_;
position = position_;
pathParent = pathParent_;
father = father_;
}
};
class HeavyLightDecomp {
private:
int N;
vector<SegmentTree> paths;
vector<NodeInfo> infos;
void computeSize(int node, vector<int> &size, vector<bool> &visited, vector<vector<int>> &tree) {
size[node] = 1;
visited[node] = true;
for(auto &son: tree[node]) {
if(!visited[son]) {
computeSize(son, size, visited, tree);
size[node] += size[son];
}
}
}
int computePaths(int node, vector<int> &size, vector<bool> &visited,
vector<vector<int>> &tree, vector<int> &cost, vector<vector<int>> &pathArrays, int pathIdx) {
visited[node] = true;
int bestSon = -1;
if((int) pathArrays.size() <= pathIdx) {
pathArrays.push_back(vector<int>());
}
pathArrays[pathIdx].push_back(cost[node]);
for(auto &son: tree[node]) {
if(!visited[son] && (bestSon == -1 || size[bestSon] < size[son])) {
bestSon = son;
}
}
if(bestSon == -1) {
return 0;
}
int addedPaths = 0;
infos[bestSon] = NodeInfo(pathIdx, infos[node].position + 1, infos[node].pathParent, node);
addedPaths += computePaths(bestSon, size, visited, tree, cost, pathArrays, pathIdx);
for(auto &son: tree[node]) {
if(!visited[son] && son != bestSon) {
infos[son] = NodeInfo(pathIdx + addedPaths + 1, 0, son, node);
addedPaths += computePaths(son, size, visited, tree, cost, pathArrays, pathIdx + addedPaths + 1) + 1;
}
}
return addedPaths;
}
public:
HeavyLightDecomp(vector<vector<int>> tree, vector<int> cost) {
N = tree.size();
infos = vector<NodeInfo>(N);
vector<int> size = vector<int>(N);
vector<bool> visited = vector<bool>(N);
vector<vector<int>> pathArrays;
computeSize(0, size, visited, tree);
fill(visited.begin(), visited.end(), false);
infos[0] = NodeInfo(0, 0, 0, -1);
computePaths(0, size, visited, tree, cost, pathArrays, 0);
for(auto &pathArray: pathArrays) {
paths.push_back(SegmentTree(pathArray));
}
}
void update(int node, int val) {
paths[infos[node].pathIdx].update(infos[node].position, val);
}
int query(int father, int son) {
int val = INT_MIN;
while(infos[father].pathIdx != infos[son].pathIdx) {
val = max(val, paths[infos[son].pathIdx].query(0, infos[son].position));
son = infos[infos[son].pathParent].father;
}
val = max(val, paths[infos[father].pathIdx].query(infos[father].position, infos[son].position));
return val;
}
};
FILE *fin, *fout;
int N, M;
vector<vector<int>> tree;
vector<int> cost;
vector<int> depth;
int dp[MAX_LOG][MAX_N];
void precalc_lca(int node, int father, int d) {
dp[0][node] = father;
depth[node] = d;
for(int i = 1; i < MAX_LOG; i++) {
dp[i][node] = dp[i-1][dp[i-1][node]];
}
for(auto &son: tree[node]) {
if(depth[son] == -1) {
precalc_lca(son, node, d + 1);
}
}
}
int lca(int x, int y) {
if(depth[x] > depth[y]) {
swap(x, y);
}
for(int i = MAX_LOG - 1; i >= 0; i--) {
if(depth[dp[i][y]] >= depth[x]) {
y = dp[i][y];
}
}
if(x == y) {
return x;
}
for (int i = MAX_LOG - 1; i >= 0; i--) {
if(dp[i][y] != dp[i][x]) {
y = dp[i][y];
x = dp[i][x];
}
}
return dp[0][x];
}
int main() {
fin = fopen("heavypath.in", "r");
fout = fopen("heavypath.out", "w");
fscanf(fin, "%d%d", &N, &M);
tree = vector<vector<int>>(N);
cost = vector<int>(N);
for(int i = 0; i < N; i++) {
fscanf(fin, "%d", &cost[i]);
}
for(int i = 0; i < N -1; i++) {
int x, y;
fscanf(fin, "%d%d", &x, &y);
x--;
y--;
tree[x].push_back(y);
tree[y].push_back(x);
}
depth = vector<int>(N, -1);
precalc_lca(0, 0, 0);
HeavyLightDecomp hpd(tree, cost);
for(int i = 0; i < M; i++) {
int type, x, y, z;
fscanf(fin, "%d%d%d", &type, &x, &y);
(void) z;
if(type == 0) {
x--;
hpd.update(x, y);
} else {
x--;
y--;
z = lca(x, y);
fprintf(fout, "%d\n", max(hpd.query(z, x), hpd.query(z, y)));
}
}
return 0;
}