#include <fstream>
#include <cstring>
#include <vector>
using namespace std;
const int N = 100005, ArbCt = 5, inf = 0x3f3f3f3f;
class ArbInt{
int arb[ArbCt * N], size;
int x, y, val;
inline int max(int a, int b){
return a > b ? a : b;
}
inline int m(int st, int dr){
return (st + dr) >> 1;
}
void update(int poz, int st, int dr){
if (st == dr){
arb[poz] = val;
return;
}
if (x <= m(st, dr))
update(poz << 1, st, m(st, dr));
else
update(1 + (poz << 1), m(st, dr) + 1, dr);
arb[poz] = max(arb[poz << 1], arb[1 + (poz << 1)]);
}
void query(int poz, int st, int dr){
if (x <= st && dr <= y){
val = max(val, arb[poz]);
return;
}
if (x <= m(st, dr))
query(poz << 1, st, m(st, dr));
if (m(st, dr) < y)
query(1 + (poz << 1), 1 + m(st, dr), dr);
}
public:
void init(int n){
size = n;
memset(arb, 0, sizeof(arb));
}
void update(int _x, int _val){
x = _x;
val = _val;
update(1, 1, size);
}
int query(int _x, int _y){
x = _x;
y = _y;
val = -inf;
query(1, 1, size);
return val;
}
};
class HeavyPath{
int lantIndex[N], arbIndex[N], depth[N], T[N], size, nrL;
vector<int> lant[N], *tree;
bool use[N];
ArbInt A;
inline int pathSeed(int x){
return lant[ lantIndex[x] ].back();
}
inline int value(int x, int y){
x = arbIndex[x]; y = arbIndex[y];
if (x > y){
int aux = x;
x = y;
y = aux;
}
return A.query(x, y);
}
int heavyPath(int x){
use[x] = true;
int Q = 1, valPath = -1, val;
for (vector<int> :: iterator it = tree[x].begin() ; it != tree[x].end() ; it++)
if (!use[*it]){
T[*it] = x;
depth[*it] = 1 + depth[x];
val = heavyPath(*it);
if (val > valPath){
valPath = val;
lantIndex[x] = lantIndex[*it];
}
Q += val;
}
if (lantIndex[x] == 0)
lantIndex[x] = ++nrL;
lant[ lantIndex[x] ].push_back(x);
return Q;
}
int lca(int x, int y){
while (x && y && lantIndex[x] != lantIndex[y])
if (depth[x] > depth[y])
x = T[ pathSeed(x) ];
else
y = T[ pathSeed(y) ];
if (x == 0)
return T[ pathSeed(y) ];
if (y == 0)
return T[ pathSeed(x) ];
return depth[x] > depth[y] ? x : y;
}
int lantQuery(int x, int L){
int val = -inf;
while (lantIndex[x] != lantIndex[L]){
val = max(val, value(x, pathSeed(x)));
x = T[ pathSeed(x) ];
}
return max(val, value(x, L));
}
public:
void buildHeavyPath(vector<int>* v, int n){
size = n;
tree = v;
depth[0] = -1;
memset(use, false, sizeof(use));
heavyPath(1);
for (int i = 1, j = 0 ; i <= nrL ; i++)
for (vector<int> :: iterator it = lant[i].begin() ; it != lant[i].end() ; it++)
arbIndex[*it] = (++j);
A.init(n);
}
void update(int x, int val){
A.update(arbIndex[x], val);
}
int query(int x, int y){
int L = lca(x, y);
return max(lantQuery(x, L), lantQuery(y, L));
}
};
vector<int> tree[N];
HeavyPath H;
int val[N], n;
ifstream in("heavypath.in");
ofstream out("heavypath.out");
int main(){
int nrU, t, x, y;
in >> n >> nrU;
for (int i = 1 ; i <= n ; i++)
in >> val[i];
for (int i = 1 ; i < n ; i++){
in >> x >> y;
tree[x].push_back(y);
tree[y].push_back(x);
}
H.buildHeavyPath(tree, n);
for (int i = 1 ; i <= n ; i++)
H.update(i, val[i]);
while (nrU--){
in >> t >> x >> y;
if (t == 0)
H.update(x, y);
else
out << H.query(x, y) << "\n";
}
return 0;
}