/**
* Worg
*/
#include <cstdio>
#include <vector>
#define leftSon (node << 1)
#define rightSon ((node << 1) + 1)
using namespace std;
FILE *fin = freopen("heavypath.in", "r", stdin);
FILE *fout = freopen("heavypath.out", "w", stdout);
const int MAX_N = 1 + 100000;
const int bufferDim = 100000;
/*-------- Input reader --------*/
class inputReader {
private:
char buffer[bufferDim];
int pos = 0;
bool digit(char c) {
return('0' <= c && c <= '9');
}
public:
void getBuffer() {
fread(buffer, 1, bufferDim, stdin);
pos = 0;
}
void operator >>(int &nr) {
nr = 0;
while(!digit(buffer[pos]))
if(++ pos == bufferDim)
getBuffer();
while(digit(buffer[pos])) {
nr = nr * 10 + buffer[pos] - '0';
if(++ pos == bufferDim)
getBuffer();
}
}
} cin;
/*-------- Input data --------*/
int N, M;
/*-------- Heavy path data --------*/
struct STree;
struct Node;
struct Chain;
struct Node {
int index, value;
int depth, weight;
vector< Node* > adjacentNodes;
Chain* chain;
};
struct Chain {
Node* father;
vector< Node* > nodes;
vector< int > maxValue;
bool ok;
int delay, maxDepth;
Chain() {
this->ok = false;
}
};
Node graph[MAX_N];
/*-------- --------*/
void readInput() {
cin.getBuffer();
cin >> N; cin >> M;
for(int i = 1; i <= N; i++) {
cin >> graph[i].value;
graph[i].index = i;
}
for(int i = 1; i < N; i++) {
int a, b; cin >> a; cin >> b;
graph[a].adjacentNodes.push_back(&graph[b]);
graph[b].adjacentNodes.push_back(&graph[a]);
}
}
void DFS(Node* node, int depth = 1, int dad = 0) {
node->depth = depth;
node->weight = 1;
Node *son;
int sonWeight = 0;
for(Node* nxt : node->adjacentNodes) {
if(dad != nxt->index) {
DFS(nxt, depth + 1, node->index);
node->weight += nxt->weight;
if(sonWeight < nxt->weight) {
sonWeight = nxt->weight;
son = nxt;
}
}
}
if(sonWeight == 0) {
/* daca nodul e frunza, atunci creem un nou lant pentru el */
node->chain = new Chain();
} else {
/* altfel, adaugam nodul curent la lantul cel mai greu din subarbore */
node->chain = son->chain;
for(Node *nxt : node->adjacentNodes) {
if(dad != nxt->index && son->index != nxt->index) {
/* nodul curent va deveni tatal celorlalte lanturi din subarbore de care nu apartine */
nxt->chain->father = node;
}
}
}
/* adaugam nodul la lista lantului
De sesizat ca pentru fiecare lant nodurile sunt introduse de jos in sus */
node->chain->nodes.push_back(node);
}
void Update(vector< int > &maxValue, int node, int left, int right, int pos, int value) {
maxValue[node] = 0;
if(left == right) {
maxValue[node] = value;
} else {
int mid = (left + right) >> 1;
if(pos <= mid) {
Update(maxValue, leftSon, left, mid, pos, value);
} else {
Update(maxValue, rightSon, mid + 1, right, pos, value);
}
maxValue[node] = max(maxValue[leftSon], maxValue[rightSon]);
}
}
void makeSegmentTrees() {
for(int i = 1; i <= N; i++) {
Chain* chain = graph[i].chain;
if(chain->ok == false) {
int Space = (int)chain->nodes.size() + 1;
chain->maxValue.reserve(Space << 2);
chain->ok = true;
Node* node = chain->nodes.back();
chain->delay = node->depth - 1;
node = chain->nodes[0];
chain->maxDepth = node->depth;
for(Node* node : chain->nodes) {
Update(chain->maxValue, 1, 1, chain->maxDepth - chain->delay, node->depth - chain->delay, node->value);
}
}
}
}
void Query(vector< int > &maxValue, int node, int left, int right, int first, int last, int &Answer) {
if(first <= left && right <= last) {
Answer = max(Answer, maxValue[node]);
} else {
int mid = (left + right) >> 1;
if(first <= mid) {
Query(maxValue, leftSon, left, mid, first, last, Answer);
}
if(mid < last) {
Query(maxValue, rightSon, mid + 1, right, first, last, Answer);
}
}
}
int getQueryAnswer(Node *u, Node *v) {
int queryAnswer = 0;
if(u->chain == v->chain) {
Chain *chain = u->chain;
Query(u->chain->maxValue, 1, 1, chain->maxDepth - chain->delay, min(u->depth, v->depth) - chain->delay, max(u->depth, v->depth) - chain->delay, queryAnswer);
} else {
int H1 = u->chain->father->depth;
int H2 = v->chain->father->depth;
if(H1 < H2) {
Chain *chain = v->chain;
Query(chain->maxValue, 1, 1, chain->maxDepth - chain->delay, 1, v->depth - chain->delay, queryAnswer);
queryAnswer = max(queryAnswer, getQueryAnswer(u, chain->father));
} else {
Chain *chain = u->chain;
Query(chain->maxValue, 1, 1, chain->maxDepth - chain->delay, 1, u->depth - chain->delay, queryAnswer);
queryAnswer = max(queryAnswer, getQueryAnswer(chain->father, v));
}
}
return queryAnswer;
}
void solveOperations() {
for(int i = 1; i <= M; i++) {
int t, x, y;
cin >> t; cin >> x; cin >> y;
if(t == 0) {
/* avem un update */
graph[x].value = y;
Chain* chain = graph[x].chain;
Update(chain->maxValue, 1, 1, chain->maxDepth - chain->delay, graph[x].depth - chain->delay, graph[x].value);
} else {
/* avem un query */
int Solution = getQueryAnswer(&graph[x], &graph[y]);
printf("%d\n", Solution);
}
}
}
int main() {
readInput();
DFS(&graph[1]);
graph[0].depth = 0; graph[1].chain->father = &graph[0];
makeSegmentTrees();
solveOperations();
return 0;
}