Link to this code: https://cses.fi/paste/e9cbd1544a84b2241185d74/
#include <iostream>
#include <vector>
 
using namespace std;
 
const int NEUTRO = 0;
 
inline int log2_floor(const int &  num){
    return __builtin_clzll(1LL) - __builtin_clzll(num);
}
 
inline int log2_ceil(const int &  num){
    return log2_floor(num) + (__builtin_popcountll(num) > 1);
}
 
inline int L(const int &  i){
    return 2*i;
}
 
inline int R(const int &  i){
    return 2*i+1;
}
 
struct segment_tree {// for maximum
    int N, numElements;
    vector<int> seg, vec;
 
    segment_tree() = default;
 
    void add(const int & num){
        vec.push_back(num);
    }
 
    void init(){
        numElements = vec.size();
        int lg = log2_ceil(numElements);
        N = (1LL << lg);
 
        seg.assign(2*N+1, -NEUTRO);
        for (int i=0; i<numElements; ++i){
            seg[N+i] = vec[i];
        }
 
        for (int i=N-1; i>=1; --i){
            seg[i] = max(seg[L(i)], seg[R(i)]);
        }
    }
 
    void set(int pos, const int &  val){ // 0-indexed
        vec[pos] = val;
 
        pos = N+pos;
        seg[pos] = val; pos /= 2;
 
        while (pos >= 1){
            seg[pos] = max(seg[L(pos)], seg[R(pos)]);
            pos /= 2;
        }
    }
 
    int get(const int &  node, const int &  l, const int &  r, const int &  a, const int &  b){
        if (b < l || r < a) return -NEUTRO;
 
        if (a <= l && r <= b){
            return seg[node];
        }
 
        int m = (l+r)/2;
        int res = -NEUTRO;
        if (a <= m){
            res = max(res, get(L(node), l, m, a, b));
        } 
        if (b >= m+1){
            res = max(res, get(R(node), m+1, r, a, b));
        }
 
        return res;
    }
 
    int get(const int &  l, const int &  r){
        return get(1, 0, N-1, l, r);
    }
};
 
const int rt=1;
const int N = 2e5+67;
const int M = 18;
int S, SD;
int t;
 
vector<int> G[N];
int par[N][M+1];
int val[N], tIn[N], tOut[N], subSz[N];
 
segment_tree heavyPaths;
int pathStart[N], pathPos[N];
 
int dfsRoot(const int & i, const int & p){
    par[i][0] = p;
 
    tIn[i] = t++;
    int sum = 1;
    for (int j : G[i]){
        if (j == p) continue;
 
        sum += dfsRoot(j, i);
    }
    tOut[i] = t++;
 
    return subSz[i] = sum;
}
 
inline bool isPar(const int &  p, const int &  s){
    return (tIn[p] <= tIn[s] && tOut[s] <= tOut[p]);
}
 
int LCA(const int &  x, const int &  y){ 
    if (isPar(x, y)){
        return x;
    } else if (isPar(y, x)){
        return y;
    }
 
    int res=x;
    for (int j=SD; j>=0; --j){
        int nw = par[res][j];
        if (!isPar(nw, y)){
            res = nw;
        }
    }
    return par[res][0];
}
 
int findHeavy(const int & i){
    int mx=-1, res=-1;
    for (const int & j : G[i]){
        if (j == par[i][0]) continue;
 
        if (subSz[j] > mx) {
            res = j;
            mx = subSz[j];
        }
    }
 
    return res;
}
 
int K=0;
void dfsHeavy(const int & i, bool heavy){ 
    int p = par[i][0];
    
    if (heavy){
        pathStart[i] = pathStart[p];
    } else {
        pathStart[i] = i;
        pathPos[i] = 0;
    }
    pathPos[i] = K++;
    heavyPaths.add(val[i]);
    
    int mxJ = findHeavy(i);
    
    if (mxJ == -1){
        return;
    }
 
    dfsHeavy(mxJ, true);
 
    for (int j : G[i]){
        if (j == p || j == mxJ) continue;
 
        dfsHeavy(j, false);
    }
}
 
int maxUpPath(int &p, int &x){
    int res=-NEUTRO; 
 
    while (pathStart[p] != pathStart[x]){
        int y = pathStart[x];
        int l = pathPos[y], r = pathPos[x];
 
        res = max(res, heavyPaths.get(l, r));
        
        x = par[y][0];
    }
 
    int l = pathPos[p], r = pathPos[x]; 
    res = max(res, heavyPaths.get(l,r));
 
    return res;
}
 
void solve(){
    int Q; cin >> S >> Q;
    SD = log2_floor(S);
 
    for (int i=1; i<=S; ++i){
        cin >> val[i];
    }
 
    for (int i=0; i<S-1; ++i){
        int u, v; cin >> u >> v;
 
        G[u].push_back(v);
        G[v].push_back(u);
    }
 
    t=0;
    dfsRoot(rt, rt);
 
    for (int j=1; j<=SD; ++j){
        for (int i=1; i<=S; ++i){
            par[i][j] = par[par[i][j-1]][j-1];
        }
    }
 
    K=0;
    dfsHeavy(rt, false);
    heavyPaths.init();
 
    while (Q--){
        int typ; cin >> typ;
 
        if (typ == 1){
            int s, x; cin >> s >> x;
            val[s] = x;
            heavyPaths.set(pathPos[s], x);
        } else if (typ == 2){
            int u, v; cin >> u >> v;
 
            int L = LCA(u,v);
            cout << max(maxUpPath(L, u), maxUpPath(L, v)) << ' ';
        }
    }
    cout << '\n';
}
 
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL);
 
    solve();
    return 0;
}