#include <stdio.h>
#include <stdlib.h>
#define MAX_N 100000
#define NIL -1
int **list;
int size[MAX_N + 1];
int val[MAX_N + 1];
template <int cmp(int, int)> class T {
private:
int *tree;
int n, ptr;
public:
T() {
}
private:
void build(int u, int left, int right, const int get) {
if (left == right) {
this -> tree[u] = val[list[get][this -> ptr]];
this -> ptr++;
return;
}
int mid = (left + right) / 2;
build(2 * u, left, mid, get);
build(2 * u + 1, mid + 1, right, get);
this -> tree[u] = cmp(this -> tree[2 * u], this -> tree[2 * u + 1]);
}
void update(int u, int left, int right, const int pos, const int val) {
if (left == right) {
this -> tree[u] = val;
return;
}
int mid = (left + right) / 2;
if (pos <= mid) {
update(2 * u, left, mid, pos, val);
} else {
update(2 * u + 1, mid + 1, right, pos, val);
}
this -> tree[u] = cmp(this -> tree[2 * u], this -> tree[2 * u + 1]);
}
int query(int u, int left, int right, const int a, const int b) {
if (a <= left && right <= b) {
return this -> tree[u];
}
int mid = (left + right) / 2;
int result = NIL;
if (a <= mid) {
result = cmp(result, query(2 * u, left, mid, a, b));
}
if (b > mid) {
result = cmp(result, query(2 * u + 1, mid + 1, right, a, b));
}
return result;
}
public:
void init(int get) {
this -> n = size[get];
this -> ptr = 0;
this -> tree = (int*)calloc(4 * n, sizeof(int));
this -> build(1, 0, n - 1, get);
}
void update(int pos, int val) {
update(1, 0, n - 1, pos, val);
}
int query(int a, int b) {
return query(1, 0, n - 1, a, b);
}
};
typedef struct {
int v, next;
} cell;
int N, M;
int adj[MAX_N + 1];
cell l[2 * MAX_N];
int numPaths;
int d[MAX_N + 1];
int pos[MAX_N + 1];
int path[MAX_N + 1];
int count[MAX_N + 1];
int begin[MAX_N + 1];
int father[MAX_N + 1];
int MAX(int X, int Y) {
return X > Y ? X : Y;
}
T <MAX> *tree;
void addEdge(int u, int v, int pos) {
l[pos].v = v;
l[pos].next = adj[u];
adj[u] = pos;
}
void dfs(int u) {
int v, ptr, son = 0;
count[u] = 1;
for (ptr = adj[u]; ptr; ptr = l[ptr].next) {
v = l[ptr].v;
if (father[v] == 0) {
father[v] = u;
d[v] = d[u] + 1;
dfs(v);
count[u] += count[v];
if (count[v] > count[son]) {
son = v;
}
}
}
if (son == 0) {
path[u] = numPaths++;
} else {
path[u] = path[son];
}
pos[u] = size[path[u]]++;
}
void heavyPath() {
// Parcurge in adancime arborele, afland caile.
father[1] = 1;
d[1] = 1;
dfs(1);
// Determina nodurile de start ale cailor.
int u;
for (u = 1; u <= N; u++) {
pos[u] = size[path[u]] - pos[u] - 1;
if (pos[u] == 0) {
begin[path[u]] = u;
}
}
}
void makePaths() {
int i, u;
// Alocarea memoriei.
list = (int**)calloc(numPaths, sizeof(int*));
for (i = 0; i < numPaths; i++) {
list[i] = (int*)calloc(size[i], sizeof(int));
size[i] = 0;
}
// Aranjeaza path-urile.
for (u = 1; u <= N; u++) {
list[path[u]][size[path[u]]] = u;
size[path[u]]++;
}
// Construieste arborii de intervale.
tree = new T <MAX> [numPaths];
for (i = 0; i < numPaths; i++) {
tree[i].init(i);
}
}
int getAnswer(int u, int v) {
// Calea lui "u" sa inceapa mai jos decat cea a lui "v".
if (d[begin[path[u]]] < d[begin[path[v]]]) {
int tmp = u;
u = v;
v = tmp;
}
// Parcurge caile pentru a afla maximul.
if (path[u] == path[v]) {
int a, b;
if (pos[u] < pos[v]) {
a = pos[u];
b = pos[v];
} else {
a = pos[v];
b = pos[u];
}
return tree[path[u]].query(a, b);
} else {
return MAX(tree[path[u]].query(0, pos[u]), getAnswer(father[begin[path[u]]], v));
}
}
int main(void) {
int i, u, v, task;
FILE *f = fopen("heavypath.in", "r");
// Citirea datelor si construirea grafului.
fscanf(f, "%d %d", &N, &M);
for (i = 1; i <= N; i++) {
fscanf(f, "%d", &val[i]);
}
for (i = 1; i < N; i++) {
fscanf(f, "%d %d", &u, &v);
addEdge(u, v, i);
addEdge(v, u, i + N - 1);
}
// Construieste caile.
heavyPath();
makePaths();
freopen("heavypath.out", "w", stdout);
while (M) {
fscanf(f, "%d %d %d", &task, &u, &v);
if (task == 0) {
tree[path[u]].update(pos[u], v);
} else {
fprintf(stdout, "%d\n", getAnswer(u, v));
}
M--;
}
fclose(f);
fclose(stdout);
return 0;
}