#include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
//#define BIG_NUM 2000000000
#define HUGE_NUM 99999999999999999
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;


#define BIG_NUM 2100000000
#define NUM 200005
#define MAX_LOG_V 18

struct Edge{
	Edge(int arg_to){
		to = arg_to;
	}
	int to;
};

struct State_makeTree{
	State_makeTree(int arg_node_id,int arg_parent,int arg_depth){
		node_id = arg_node_id;
		parent = arg_parent;
		depth = arg_depth;
	}
	int node_id,parent,depth;
};

struct State_HLD{
	State_HLD(int arg_node_id,int arg_head,int arg_parent,int arg_line_id){
		node_id = arg_node_id;
		head = arg_head;
		parent = arg_parent;
		line_id = arg_line_id;
	}
	bool operator<(const struct State_HLD &arg) const{
		return line_id > arg.line_id; //★★heav_edgeのラインidの昇順(★★同じラインを連番にするため★★)
	}
	int node_id,head,parent,line_id;
};

struct Info{
	Info(){
		all_value = left_value = right_value = mid_value = 0;
	}
	Info(ll arg_all_value,ll arg_left_value,ll arg_right_value,ll arg_mid_value){
		all_value = arg_all_value;
		left_value = arg_left_value;
		right_value = arg_right_value;
		mid_value = arg_mid_value;
	}
	ll all_value,left_value,right_value,mid_value;
};

struct Data{
	int parent,head,depth,weight,line_id;
};

struct Info_LCA{
	int lca,second;
};

int V,N;
int root,global_index;
int index_table[NUM],out_num[NUM];
int num_line;
int POW[MAX_LOG_V];
int parent[MAX_LOG_V][NUM];
ll first_value[NUM],table[NUM*8];

Data data[NUM];
vector<Edge> G[NUM];

void init(int first_N){

	while(N < first_N)N *= 2;
}

void makeTree(){

	queue<State_makeTree> Q;
	Q.push(State_makeTree(root,-1,0));

	int child;

	while(!Q.empty()){

		data[Q.front().node_id].parent = Q.front().parent;
		data[Q.front().node_id].depth = Q.front().depth;
		out_num[Q.front().node_id] = G[Q.front().node_id].size();

		for(int i = 0; i < G[Q.front().node_id].size(); i++){

			child = G[Q.front().node_id][i].to;
			if(child == Q.front().parent)continue;

			Q.push(State_makeTree(child,Q.front().node_id,Q.front().depth+1));
		}
		Q.pop();
	}
}

//葉から、ボトムアップにweightを計算する
void calc_weight(int first_N){

	queue<int> Q;

	for(int i = 0; i < first_N; i++){
		if(out_num[i] == 1){
			Q.push(i);
		}
	}

	int child;

	while(!Q.empty()){

		data[Q.front()].weight = 1;

		for(int i = 0; i < G[Q.front()].size(); i++){

			child = G[Q.front()][i].to;
			if(child == data[Q.front()].parent)continue;

			data[Q.front()].weight += data[child].weight;
		}

		if(data[Q.front()].parent != -1){

			out_num[data[Q.front()].parent]--;
			if(data[Q.front()].parent != root && out_num[data[Q.front()].parent] == 1){

				Q.push(data[Q.front()].parent);

			}else if(data[Q.front()].parent == root && out_num[data[Q.front()].parent] == 0){

				Q.push(data[Q.front()].parent);
			}
		}
		Q.pop();
	}
}

void HLD(){

	num_line = 0;
	priority_queue<State_HLD> Q;
	data[root].line_id = num_line;

	Q.push(State_HLD(root,root,-1,num_line++));

	int max_weight,max_node_id,child;

	while(!Q.empty()){

		index_table[Q.top().node_id] = global_index++; //★★同一ラインに属するノード木が、セグ木上で連番になるようにする★★
		data[Q.top().node_id].head = Q.top().head;
		data[Q.top().node_id].line_id = Q.top().line_id;

		max_weight = -BIG_NUM;
		max_node_id = -1;

		for(int i = 0; i < G[Q.top().node_id].size(); i++){

			child = G[Q.top().node_id][i].to;
			if(child == Q.top().parent)continue;
			if(max_weight < data[child].weight){
				max_weight = data[child].weight;
				max_node_id = child;
			}
		}
		if(max_node_id == -1){
			Q.pop();
			continue;
		}

		//最大の子
		Q.push(State_HLD(max_node_id,Q.top().head,Q.top().node_id,Q.top().line_id));

		//その他の子
		for(int i = 0; i < G[Q.top().node_id].size(); i++){

				child = G[Q.top().node_id][i].to;
				if(child == Q.top().parent || child == max_node_id)continue;

				//★新しいラインを作成する★
				data[child].line_id = num_line;
				Q.push(State_HLD(child,child,Q.top().node_id,num_line));
				num_line++;
		}
		Q.pop();
	}
}

Info merge(Info L,Info R){

	//★L,Rが同時に範囲外ということはないはず★
	if(R.all_value == -BIG_NUM)return L;
	if(L.all_value == -BIG_NUM)return R;

	Info ret;

	ret.all_value = L.all_value+R.all_value;
	ret.left_value = max(L.left_value,L.all_value+R.left_value);
	ret.right_value = max(R.right_value,R.all_value+L.right_value);
	ret.mid_value = max(max(L.mid_value,R.mid_value),L.right_value+R.left_value);

	return ret;
}

Info query(int search_left,int search_right,int node_id,int node_left,int node_right){

	if(search_right < node_left || search_left > node_right){ //区間の重なりがない場合

		return Info(-BIG_NUM,-BIG_NUM,-BIG_NUM,-BIG_NUM); //範囲外であることを伝える

	}else{

		if(table[node_id] == -BIG_NUM){ //区間の重なりはあるが、一様性が壊れている場合→データは子が持っているので、子にクエリを投げる

			Info left = query(search_left,search_right,2*node_id+1,node_left,(node_left+node_right)/2);
			Info right = query(search_left,search_right,2*node_id+2,(node_left+node_right)/2+1,node_right);
			return merge(left,right);

		}else{	//区間の重なりがあり、かつ一様性が壊れていない場合

			ll tmp = (min(search_right,node_right)-max(search_left,node_left)+1)*table[node_id];

			if(table[node_id] < 0){ //★区間の値が不なら、値は1つしか使わない方が良い(all_value以外)★

				return Info(tmp,table[node_id],table[node_id],table[node_id]);

			}else{ //区間の値が0以上なら、全ての値を使った方が良い

				return Info(tmp,tmp,tmp,tmp);
			}
		}
	}
}

Info_LCA LCA(int left,int right){

	Info_LCA ret;

	for(int k = MAX_LOG_V-1; k >= 0; k--){
		if(data[right].depth-data[left].depth >= POW[k]){
			right = parent[k][right];
		}
	}

	if(left == right){

		ret.lca = left;
		return ret;
	}

	for(int k = MAX_LOG_V-1; k >= 0; k--){
		if(parent[k][left] == parent[k][right]){
			//Do nothing
		}else{
			left = parent[k][left];
			right = parent[k][right];
		}
	}
	ret.lca = parent[0][right];
	ret.second = left;

	return ret;
}

void init_parent(int first_N){

	for(int k = 0; k + 1 <= 17; k++){
		for(int node_id = 0; node_id < first_N; node_id++){
			if(parent[k][node_id] < 0)parent[k+1][node_id] = -1; //node_idの2^k上のノードがルートノードより上なら、2^(k+1)上も同様とする
			else{
				parent[k+1][node_id] = parent[k][parent[k][node_id]];
			}
		}
	}
}

void update(int update_left,int update_right,int new_value,int node_id,int node_left,int node_right){
	if(update_right < node_left || update_left > node_right)return;
	else if(update_left <= node_left && update_right >= node_right){
		table[node_id] = new_value;
	}else{

		if(table[node_id] != -BIG_NUM){
			table[2*node_id+1] = table[node_id];
			table[2*node_id+2] = table[node_id];
			table[node_id] = -BIG_NUM;
		}
		update(update_left,update_right,new_value,2*node_id+1,node_left,(node_left+node_right)/2);
		update(update_left,update_right,new_value,2*node_id+2,(node_left+node_right)/2+1,node_right);
	}
}

//片道
void calc_update(int node_id,int terminal_node,ll next_cost){

	int head_node;

	while(true){

		head_node = data[node_id].head;

		//★★機械的にheadを設定すると、teriminal_nodeを飛び越えてしまう!!★★
		if(data[head_node].depth <= data[terminal_node].depth){
			head_node = terminal_node;
		}

		update(index_table[head_node],index_table[node_id],next_cost,0,0,N-1);

		if(head_node == terminal_node)break;

		node_id = data[head_node].parent;
	}
}



void update(int from,int to,ll next_cost){

	//★toを深くする(LCA関数に合わせる)
	if(data[from].depth > data[to].depth){
		swap(from,to);
	}

	Info_LCA info_lca = LCA(from,to);

	if(info_lca.lca != from && info_lca.lca != to){ //LCAが第3のノードとして存在する場合

		calc_update(from,info_lca.second,next_cost);
		calc_update(to,info_lca.lca,next_cost);

	}else{ //片方がもう一方の祖先である場合(toの方が深いので、from)

		calc_update(to,info_lca.lca,next_cost);

	}
}

ll get_maximum(Info info){

	return max(max(info.all_value,info.left_value),max(info.right_value,info.mid_value));
}


//片道
Info get_cost(int node_id,int terminal_node){

	Info ret,tmp;
	int head_node;
	ret.all_value = -BIG_NUM; //範囲外にしておく

	while(true){

		head_node = data[node_id].head;

		//★★機械的にheadを設定すると、teriminal_nodeを飛び越えてしまう!!★★
		if(data[head_node].depth <= data[terminal_node].depth){
			head_node = terminal_node;
		}

		tmp = query(index_table[head_node],index_table[node_id],0,0,N-1);

		ret = merge(tmp,ret); //★★tmpの方が根方向(左)★★

		if(head_node == terminal_node)break;

		node_id = data[head_node].parent;
	}

	return ret;
}


ll get_max_value(int from,int to){

	//★toを深くする(LCA関数に合わせる)
	if(data[from].depth > data[to].depth){
		swap(from,to);
	}

	Info_LCA info_lca = LCA(from,to);

	if(info_lca.lca != from && info_lca.lca != to){ //LCAが第3のノードとして存在する場合

		Info A,B;

		A = get_cost(from,info_lca.second);
		B = get_cost(to,info_lca.lca);

		//★両方とも、lca側がleftになっているため、ABと並べるためAの左右をswap★
		swap(A.left_value,A.right_value);

		return get_maximum(merge(A,B));

	}else{ //片方がもう一方の祖先である場合(toの方が深いので、from)

		return get_maximum(get_cost(to,info_lca.lca));
	}
}

int main(){

	POW[0] = 1;
	for(int i = 1; i < MAX_LOG_V; i++){
		POW[i] = POW[i-1]*2;
	}

	int first_N,num_query;
	scanf("%d %d",&first_N,&num_query);

	for(int i = 0; i < first_N; i++){
		scanf("%lld",&first_value[i]);
	}

	int from,to;
	ll cost;

	for(int loop = 0; loop < first_N-1; loop++){

		scanf("%d %d",&from,&to);
		from--;
		to--;

		G[from].push_back(Edge(to));
		G[to].push_back(Edge(from));
	}

	root = 0;
	makeTree();
	calc_weight(first_N);

	global_index = 0;
	HLD();

	for(int i = 0; i < first_N; i++){
		parent[0][i] = data[i].parent;
	}
	init_parent(first_N);

	N = 1;
	init(first_N);

	for(int i = 0; i <= 2*N-2; i++){

		table[i] = -BIG_NUM; //★値を持たない状態にしておく★
	}

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

		update(index_table[i],index_table[i],first_value[i],0,0,N-1);
	}

	int command;

	for(int loop = 0; loop < num_query; loop++){

		scanf("%d %d %d %lld",&command,&from,&to,&cost);
		from--;
		to--;

		if(command == 1){

			update(from,to,cost);

		}else{

			printf("%lld\n",get_max_value(from,to));
		}
	}

	return 0;
}



Measured memory_bytes: 39864