#include <bits/stdc++.h>
#define GET_MACRO(_1,_2,_3,_4,_5,NAME,...) NAME
#define pr(...) GET_MACRO(__VA_ARGS__,pr5,pr4,pr3,pr2,pr1)(__VA_ARGS__)
#define pr1(a) (#a)<<"="<<(a)
#define pr2(a,b) pr1(a)<<", "<<pr1(b)
#define pr3(a,b,c) pr2(a,b)<<", "<<pr1(c)
#define pr4(a,b,c,d) pr3(a,b,c)<<", "<<pr1(d)
#define pr5(a,b,c,d,e) pr4(a,b,c,d)<<", "<<pr1(e)
#define int long long
#define double long double
using namespace std;
const int N = 100010;
const int INF = 1LL<<55;
const int mod = (1e9)+7;
const double EPS = 1e-8;
const double PI = 6.0 * asin(0.5);
typedef pair<int,int> P;
typedef long long ll;
template<class T> T Max(T &a,T b){return a=max(a,b);}
template<class T> T Min(T &a,T b){return a=min(a,b);}
ostream& operator<<(ostream& o,P p){return o<<"("<<p.first<<","<<p.second<<")";}
istream& operator>>(istream& i,P &p){return i>>p.first>>p.second;}
ostream& operator<<(ostream& o,vector<auto> &a){int i=0;for(auto t:a)o<<(i++?" ":"")<<t;return o;}
istream& operator>>(istream& i,vector<auto> &a){for(auto &t:a)i>>t;return i;}
void prArr(auto a,string s=" "){int i=0;for(auto t:a)cout<<(i++?s:"")<<t;cout<<endl;}

class HLD{
public:
  int V;                     //ノード数
  int root;                  //根の頂点番号
  vector<vector<int> > G;    //Grapth
  vector<int> par;           //parent: par[元の頂点番号]->元の頂点番号
  vector<int> Hchild;        //各ノードから伸びるHeavy Childの頂点番号: Hedge[元の頂点番号]->元の頂点番号
  vector<int> number;        //各ノードに割り当てなおした頂点番号: number[元の頂点番号]->振り直した頂点番号
  vector<int> id;            //割り当て直した数字の元のノード番号: id[振り直した頂点番号]->元の頂点番号
  vector<int> group;         //各ノードが所属するグループ番号: group[元の頂点番号]->元の頂点番号
  vector<int> depth;         //根からの各ノードの深さ: depth[元の頂点番号]
  bool ok;                   //build()が済んでいるか
  
  HLD(){root = V = -1;}
  HLD(int V):V(V), root(0), G(V), par(V,-1), Hchild(V,-1), 
             number(V,-1),id(V,-1),group(V,-1),depth(V,0),ok(false){}
  
  void add_edge(int a,int b){
    assert(a < V && b < V);
    G[a].push_back(b);
    G[b].push_back(a);
  }
  
  void build(int _root = 0){
    root = _root;
    int num = 0;
    ok = true;
    dfs1(); numbering(num); dfs2();
  }

  int dfs1(int pos = -1,int pre = -1){
    if(pos == -1) pos = root;
    int mx = -1, weight = 1;
    for(int to:G[pos]){
      if(to == pre) continue;
      int cweight = dfs1(to,pos);
      weight += cweight;
      if(mx < cweight) Hchild[pos] = to, mx = cweight;
    }
    return weight;
  }
  
  void dfs2(int pos = -1,int pre = -1,int dep = 0,int g = -1){
    if(pos == -1) pos = g = root;
    par[pos] = pre;
    depth[pos] = dep;
    group[pos] = g;
    for(int to:G[pos]) if(to!=pre) dfs2(to,pos,dep+1,Hchild[pos]==to? g:to);
  }
  
  void numbering(int &cnt,int pos = -1,int pre = -1){
    if(pos == -1) pos = root;
    number[pos] = cnt++;
    id[number[pos]] = pos;
    if(Hchild[pos]>=0) numbering(cnt,Hchild[pos],pos);
    for(int to:G[pos])if(to!=pre && to!=Hchild[pos]) numbering(cnt,to,pos);
  }
  
  typedef pair<int,int> P;
  vector<P> paths;  //[u,v]間の経路をいくつかの「閉区間の連番の経路」に分解する。(最大logN個に分解される)
  vector<P> Ledges; //paths同士を結ぶライトエッジ
  void buildPaths(int u,int v){
    if(depth[group[u]] < depth[group[v]]) swap(u,v);// 深い方のノードから処理する。
    int nu=number[u], nv=number[v];
    if(same(u,v)){ paths.push_back(P(min(nu,nv),max(nu,nv)));return;} 

    int gu = group[u];
    buildPaths(par[gu],v);

    paths.push_back(P(number[gu], number[u]));
    Ledges.push_back(P(number[par[gu]], number[gu]));
  }
  
  //lca(元の頂点番号,元の頂点番号) -> 元の頂点番号
  int lca(int u,int v){
    assert(ok && "Please Build");
    if(depth[group[u]] < depth[group[v]]) swap(u,v);
    if(same(u,v)) return depth[u] < depth[v]? u:v;
    return lca(par[group[u]],v);
  }
  
  //getPath(元の頂点番号,元の頂点番号) -> 振り直した頂点番号
  pair<vector<P>, vector<P> > getPath(int u,int v){
    assert(ok && "Please Build");
    paths.clear();
    Ledges.clear();
    buildPaths(u,v);
    return make_pair(paths,Ledges);
  }

  /*(元の頂点番号,元の頂点番号)*/ 
  bool same(int a,int b){return group[a] == group[b];}
};


  //比較可能な主データ型
struct D{
  int l,m,r,sum;
  D():l(-INF),m(-INF),r(-INF),sum(0){} /*適切な値にする!!!!!!*/
  D(int l,int m,int r,int sum):l(l),m(m),r(r),sum(sum){};
  //bool operator < (D a)const {return value < a.value;} //merge用
};

class Seg{
public: 
  
  //遅延用の型  
  struct T{
    bool type; //0 - empty   , 1 - update
    int value;
    T():type(0),value(0){}
    T(bool type,int value):type(type),value(value){}
  };

  
  //D INF = D(1LL<<55); /*!!!!!!!!!!!!!!!!!!!!!!!!*/

  int n;
  vector<D> dat;
  vector<T> td;
  
  Seg(){n=-1;}
  Seg(int n_){
    n=1;
    while(n<n_)n*=2;
    td.resize(2*n-1,T());
    dat.resize(2*n-1,D());
  }
  
  D merge(D a,D b){
    D res;
    res.l = max(a.l, a.sum + b.l);
    res.r = max(b.r, a.r + b.sum);
    res.m = max({ a.m, b.m, a.r + b.l});
    res.sum = a.sum + b.sum;
    return res;
  }
  
  void delay(int k,int len){
    if(td[k].type==0) return;
    int x = td[k].value;
    td[k].type = 0;
    td[k].value = 0;
    
    int l = k*2+1,r = k*2+2;
    {
      dat[l].l = max(x, len * x);
      dat[l].m = max(x, len * x);
      dat[l].r = max(x , len * x);
      dat[l].sum = x * len;
      td[l].type = 1;
      td[l].value = x;
    }
    
    {
      dat[r].l = max(x, len * x);
      dat[r].m = max(x, len * x);
      dat[r].r = max(x, len * x);
      dat[r].sum = x * len;
      td[r].type = 1;
      td[r].value = x;
    }
  }
  
  D update(int k,int x,int len){
    td[k].type = 1;
    td[k].value = x;
    dat[k].l = max(x, len * x);
    dat[k].m = max(x, len * x);
    dat[k].r = max(x, len * x);
    dat[k].sum = x * len;
    
    return dat[k];
  }
  
  //[a,b)の値をx加算　add(a,b,x)
  D query(int a,int b,int x,bool flg=true,int k=0,int l=0,int r=-1){
    if(r==-1) r=n, assert(a <= n && b <= n);
    if(r<=a||b<=l) return flg? dat[k]:D();
    if(a<=l&&r<=b) return flg? update(k,x,r - l):dat[k];
    
    delay(k, (r - l) / 2);
    D vl = query(a,b,x,flg,k*2+1,l,(l+r)/2);
    D vr = query(a,b,x,flg,k*2+2,(l+r)/2,r);
    if(flg) dat[k] = merge(vl,vr);
    return merge(vl,vr);
  }
  
  //[a,b)の最小値を得る　find(a,b);
  D find(int a,int b){
    D res = query(a,b,0,false);
    return res;
  }
};


signed main(){

  int n,q;
  cin>>n>>q;
  
  vector<int> W(n);
  cin>>W;

  HLD hld(n);
  for(int i=0;i<n-1;i++){
    int a,b;
    cin>>a>>b; a--,b--;
    hld.add_edge(a,b);
  }

  srand((unsigned)time(NULL));
  hld.build(rand() % n);
  Seg seg(n);
  
  for(int i=0;i<n;i++) {
    int num = hld.number[i];
    seg.query(num,num + 1,W[i]);
  }
  
  auto update = [&](int a,int b,int c){
    auto p = hld.getPath(a,b);
    auto path = p.first;
    
    for(P p:path){
      int l = p.first;
      int r = p.second;
      //cout<<"update: "<<pr(l,r)<<endl;
      seg.query(l,r+1,c);
    }
  };

  auto calc = [&](int a,int b){
    int lca = hld.lca(a,b);
    D res;
    auto A = hld.getPath(a,lca).first;
    auto B = hld.getPath(b,lca).first;

    if(A.back().second > B.back().second) swap(A,B);
    reverse(A.begin(),A.end());
    
    for(auto p:A){
      int l = p.first;
      int r = p.second;
      D d = seg.find(l, r+1);
      //cout<<"calc1: "<<pr(hld.id[l],hld.id[r],lca)<<endl;
      //cout<<"d1: "<<pr(d.l, d.m, d.r, d.sum)<<endl<<endl;
      swap(d.l,d.r);
      res = seg.merge(res, d);

    }

    B[0].first++;
    //if(B[0].first == B[0].second) B.erase(B.begin());
    
    for(auto p:B){
      int l = p.first;
      int r = p.second;
      D d = seg.find(l, r+1);
      //cout<<"calc2: "<<pr(hld.id[l],hld.id[r],lca)<<endl;
      //cout<<"d2: "<<pr(d.l, d.m, d.r, d.sum)<<endl<<endl;
      res = seg.merge(res,d);
    }
    //cout<<pr(res.l,res.m,res.r,res.sum)<<endl;
    return res.m;
  };

  while(q--){
    int cmd,a,b,c;
    cin>>cmd>>a>>b>>c; a--,b--;
    
    if(cmd == 1) update(a,b,c);

    if(cmd == 2){
      int ans = calc(a,b);
      cout<<ans<<endl;
    }
  }
  
  return 0;
}



Measured memory_bytes: 93640