// Slower C++ solution:
#define NDEBUG



#include <iostream>

#include <cstring>

#include <algorithm>

#include <vector>

#include <map>

#include <queue>

#include <set>

#include <tuple>

#include <cassert>



using namespace std;

  

typedef long long ll;

 

struct LCTree {

    typedef int D;

    static const int INF = 1LL<<25;

    static const int DF = 1LL<<25;

    struct Node;

    typedef Node* NP;

    static Node last_d;

    static NP last;

    struct Node {

        NP p, l, r;

        int sz;

        int id, v, sm, lz;

        int lsm, rsm, ma, mv;

        bool rev;

        Node(D id, D v) :p(NULL), l(last), r(last), sz(1),

            id(id), v(v), sm(v), lz(DF), rev(false),

            lsm(max(v, 0)), rsm(max(v, 0)), ma(max(v, 0)), mv(v) {}

        Node(string s) : l(NULL), r(NULL), sz(0), id(-1), v(-INF), mv(-INF) {}

        inline int pos() {

            if (p) {

                if (p->l == this) return -1;

                if (p->r == this) return 1;

            }

            return 0;

        }

        void rot() {

            NP q = p, qq = q->p;

            int qp = q->pos();

            if (q->l == this) {

                q->l = r; r->p = q;

                r = q; q->p = this;

            } else {

                q->r = l; l->p = q;

                l = q; q->p = this;

            }

            q->update(); update();

            p = qq;

            if (!qp) return;

            if (qp == -1) {

                qq->l = this;

            } else {

                qq->r = this;

            }

            qq->update();

        }

        void splay() {

            assert(this != last);

            supush();

            int ps;

            while ((ps = pos())) {

                int pps = p->pos();

                int u = ps*p->pos();

                if (!pps) {

                    rot();

                } else if (ps == pps) {

                    p->rot(); rot();

                } else {

                    rot(); rot();

                }

            }

        }

        void expose() {

            assert(this != last);

            NP u = this;

            do {

                u->splay();

            } while ((u = u->p));

            u = last;

            u->p = this;

            do {

                u->p->r = u;

                u->p->update();

                u = u->p;

            } while (u->p);

            splay();

        }

        void push() {

            if (rev) {

                if (l->sz) {

                    l->revdata();

                }

                if (r->sz) {

                    r->revdata();

                }

                rev = false;

            }

 

            if (lz != DF) {

                if (l->sz) {

                    l->lzdata(lz);

                }

                if (r->sz) {

                    r->lzdata(lz);

                }

                lz = DF;

            }

        }

        void supush() {

            if (pos()) {

                p->supush();

            }

            push();

        }

        void revdata() {

            swap(l, r);

            swap(lsm, rsm);

            rev ^= true;

        }

        void lzdata(D d) {

            v = d;

            sm = sz*d;

            lsm = rsm = ma = max(0, sm);

            mv = d;

            lz = d;

        }

        NP update() {

            assert(this != last);

            sz = 1+l->sz+r->sz;

            sm = v + l->sm + r->sm;

            lsm = max(l->lsm, l->sm+v+r->lsm);

            rsm = max(r->rsm, r->sm+v+l->rsm);

            ma = max(l->ma, r->ma);

            ma = max(ma, l->rsm+v+r->lsm);

            mv = max(l->mv, r->mv);

            mv = max(mv, v);

            return this;

        }

    } *n;

    LCTree() : n(last) {}

    LCTree(NP n) : n(n) {}

    LCTree(D id, D d) : n(new Node(id, d)) {}

    int sz() {

        return n->sz;

    }

    void event() {

        n->expose();

        n->revdata();

    }

    void link(LCTree r) {

        event();

        r.n->expose();

        n->p = r.n;

    }

 

    void set(int x) {

        n->expose();

        n->lzdata(x);

    }

    int get() {

        n->expose();

        if (n->mv < 0) return n->mv;

        return n->ma;

    }

};

LCTree::Node LCTree::last_d = LCTree::Node("last");

LCTree::NP LCTree::last = &last_d;

 

  

const int MN = 201000;

LCTree tr[MN];

int main() {

    int n, q;

    scanf("%d %d", &n, &q);

    for (int i = 0; i < n; i++) {

        int w;

        scanf("%d", &w);

        tr[i] = LCTree(i, w);

    }

    for (int i = 0; i < n-1; i++) {

        int s, e;

        scanf("%d %d", &s, &e); s--; e--;

        tr[s].link(tr[e]);

    }

    for (int i = 0; i < q; i++) {

        int t, a, b, c;

        scanf("%d %d %d %d", &t, &a, &b, &c); a--; b--;

        if (t == 1) {

            tr[a].event();

            tr[b].set(c);

        } else {

            tr[a].event();

            printf("%d\n", tr[b].get());

        }

    }

}

// Faster C++ solution for the same problem (measured speedup: 2.4994596995747593x):
#include <bits/stdc++.h>

 

using namespace std;

using ll = long long;

constexpr ll TEN(int n) { return (n==0) ? 1 : 10*TEN(n-1); }

template<class T> using V = vector<T>;

template<class T> using VV = V<V<T>>;



struct HL {

    V<int> _ord, _rord, big, small;

    HL(int n = 0) : _ord(n), _rord(n), big(n), small(n) {}

    int ord(int i) { return i == -1 ? -1 : _ord[i]; }

    int rord(int i) { return i == -1 ? -1 : _rord[i]; }

    int par(int a) {

        a = ord(a);

        return rord(small[a] == a ? big[a] : a-1);

    }

    int lca(int a, int b) {

        a = ord(a); b = ord(b);

        while (a != b) {

            if (a > b) swap(a, b);

            if (small[b] <= a) break;

            else b = big[b];

        }

        return rord(a);

    }

    void get_path(int a, int b, function<void(int, int)> f) { // a<-b

        a = ord(a); b = ord(b);

        while (a < b) {

            if (small[b] <= a) f(a+1, b);

            else f(small[b], b);

            b = big[b];

        }

    }

};

template<class E> struct HL_EXEC : HL {

    const VV<E>& g;

    V<int> tch;

    int id = 0;

    HL_EXEC(const VV<E>& _g, int r) : HL(_g.size()), g(_g), tch(g.size(), -1) {

        assert(dfs_sz(r, -1) == int(g.size()));

        dfs(r, -1);

    }

    int dfs_sz(int p, int b) {

        int sz = 1, msz = -1;

        for (auto e: g[p]) {

            if (e.to == b) continue;

            int u = dfs_sz(e.to, p);

            sz += u;

            if (msz < u) tie(tch[p], msz) = make_pair(e.to, u);

        }

        return sz;

    }

    void dfs(int p, int b) {

        int q = id++, bq = ord(b);

        _ord[p] = q; _rord[q] = p;

        if (b == -1 || bq != q-1) {

            small[q] = q; big[q] = bq;

        } else {

            small[q] = small[bq]; big[q] = big[bq];

        }

        if (tch[p] == -1) return;

        dfs(tch[p], p);

        for (auto e: g[p]) {

            if (e.to == b || e.to == tch[p]) continue;

            dfs(e.to, p);

        }

    }

};

template<class E> HL get_hl(const VV<E>& g, int r) { return HL_EXEC<E>(g, r); }



struct D {

    ll mx = -TEN(9), l = -TEN(9), r = -TEN(9), sm = 0;

    D operator+(const D& ri) const {

        return D{max({mx, ri.mx, r+ri.l}), max(l, sm+ri.l), max(r+ri.sm, ri.r), sm+ri.sm};

    }

};

struct Node {

    using NP = Node*;

    NP l, r;

    int sz;

    D d; int lz = -TEN(9);

    Node(int _sz, V<ll> &fi, int off = 0) : sz(_sz) {

        if (sz == 1) {

            lzdata(fi[off]);

            return;

        }

        l = new Node(sz/2, fi, off);

        r = new Node(sz - sz/2, fi, off + sz/2);

        d = l->d + r->d;

    }

    void lzdata(ll x) {

        if (x >= 0) d = D{x*sz, x*sz, x*sz, x*sz};

        else d = D{x, x, x, x*sz};

        lz = x;

    }

    void push() {

        if (lz == -TEN(9)) return;

        l->lzdata(lz); r->lzdata(lz);

        lz = -TEN(9);

    }

    D get(int a, int b) {

        if (b <= 0 || sz <= a) return D();

        if (a <= 0 && sz <= b) return d;

        push();

        return l->get(a, b) + r->get(a-sz/2, b-sz/2);

    }

    void fil(int a, int b, int x) {

        if (b <= 0 || sz <= a) return;

        if (a <= 0 && sz <= b) {

            lzdata(x);

            return;

        }

        push();

        l->fil(a, b, x); r->fil(a-sz/2, b-sz/2, x); 

        d = l->d + r->d;       

    }

};



struct E { int to; };

int main() {

    ios::sync_with_stdio(false); cin.tie(0);

    int n, q;

    scanf("%d %d", &n, &q);

//    cin >> n >> q;

    V<ll> w(n), w2(n);

    for (int i = 0; i < n; i++) scanf("%lld", &(w[i]));

    VV<E> g(n);

    for (int i = 0; i < n-1; i++) {

        int a, b;

        scanf("%d %d", &a, &b); a--; b--;

//        cin >> a >> b; a--; b--;

        g[a].push_back(E{b});

        g[b].push_back(E{a});

    }

    auto hl = get_hl(g, 0);    

    for (int i = 0; i < n; i++) w2[i] = w[hl.rord(i)];

    auto tr = new Node(n, w2);

    for (int i = 0; i < q; i++) {

        int t, a, b; ll x;

        scanf("%d %d %d %lld", &t, &a, &b, &x); a--; b--;

//        cin >> t >> a >> b >> x; a--; b--;

        int c = hl.lca(a, b);

        int d = hl.par(c);

        if (t == 1) {

            hl.get_path(c, a, [&](int l, int r){ tr->fil(l, r+1, x); });

            hl.get_path(d, b, [&](int l, int r){ tr->fil(l, r+1, x); });

        } else {

            D ld, rd;

            hl.get_path(c, a, [&](int l, int r){ ld = tr->get(l, r+1) + ld; });

            hl.get_path(d, b, [&](int l, int r){ rd = tr->get(l, r+1) + rd; });

            swap(rd.l, rd.r);

            printf("%lld\n", (rd + ld).mx);

//            cout << (rd + ld).mx << '\n';

        }

    }

    return 0;

}