#define PROBLEM "https://judge.yosupo.jp/problem/point_set_tree_path_composite_sum_fixed_root"
//#include"../../template/template.hpp"//#include"../../modint/montgomery-modint.hpp"//#include"../../tree/static-top-tree-vertex-based.hpp"//usingmint=LazyMontgomeryModInt<998244353>;structPath{minta,b,s,x;};structPoint{mints,x;};vector<mint>calc(intN,intQ,vector<int>A,vector<int>U,vector<int>V,vector<int>B,vector<int>C,vector<int>cmd,vector<int>I,vector<int>X,vector<int>Y){vector<vector<int>>g(2*N-1);rep(e,N-1){g[N+e].push_back(U[e]),g[U[e]].push_back(N+e);g[N+e].push_back(V[e]),g[V[e]].push_back(N+e);}autovertex=[&](inti)->Path{if(i<N)return{1,0,A[i],1};inte=i-N;return{B[e],C[e],0,0};};autocompress=[&](constPath&p,constPath&c)->Path{return{p.a*c.a,p.a*c.b+p.b,p.s+p.a*c.s+p.b*c.x,p.x+c.x};};autorake=[&](constPoint&a,constPoint&b)->Point{return{a.s+b.s,a.x+b.x};};autoadd_edge=[&](constPath&a)->Point{return{a.s,a.x};};autoadd_vertex=[&](constPoint&a,inti)->Path{if(i<N)return{1,0,a.s+A[i],a.x+1};inte=i-N;return{B[e],C[e],a.s*B[e]+a.x*C[e],a.x};};HeavyLightDecompositionhld{g};DPonStaticTopTreeVertexBased<vector<vector<int>>,Path,Point,decltype(vertex),decltype(compress),decltype(rake),decltype(add_edge),decltype(add_vertex)>dp(hld,vertex,compress,rake,add_edge,add_vertex);vector<mint>ans(Q);rep(q,Q){if(cmd[q]==0){A[I[q]]=X[q];dp.update(I[q]);}else{B[I[q]]=X[q],C[I[q]]=Y[q];dp.update(N+I[q]);}ans[q]=dp.get().s;}returnans;}voidNyaan::solve(){intN,Q;in(N,Q);vector<int>A(N);for(auto&x:A)in(x);vector<int>U(N-1),V(N-1),B(N-1),C(N-1);rep(i,N-1)in(U[i],V[i],B[i],C[i]);vector<int>cmd(Q),I(Q),X(Q),Y(Q,-1);rep(i,Q){in(cmd[i],I[i],X[i]);if(cmd[i]==1)in(Y[i]);}autoac=calc(N,Q,A,U,V,B,C,cmd,I,X,Y);for(auto&x:ac)out(x.get());}
#line 1 "verify/verify-yosupo-graph/yosupo-point-set-tree-path-composite-sum-fixed-root.test.cpp"
#define PROBLEM "https://judge.yosupo.jp/problem/point_set_tree_path_composite_sum_fixed_root"
//#line 2 "template/template.hpp"
usingnamespacestd;// intrinstic#include<immintrin.h>#include<algorithm>
#include<array>
#include<bitset>
#include<cassert>
#include<cctype>
#include<cfenv>
#include<cfloat>
#include<chrono>
#include<cinttypes>
#include<climits>
#include<cmath>
#include<complex>
#include<cstdarg>
#include<cstddef>
#include<cstdint>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<deque>
#include<fstream>
#include<functional>
#include<initializer_list>
#include<iomanip>
#include<ios>
#include<iostream>
#include<istream>
#include<iterator>
#include<limits>
#include<list>
#include<map>
#include<memory>
#include<new>
#include<numeric>
#include<ostream>
#include<queue>
#include<random>
#include<set>
#include<sstream>
#include<stack>
#include<streambuf>
#include<string>
#include<tuple>
#include<type_traits>
#include<typeinfo>
#include<unordered_map>
#include<unordered_set>
#include<utility>
#include<vector>// utility#line 1 "template/util.hpp"
namespaceNyaan{usingll=longlong;usingi64=longlong;usingu64=unsignedlonglong;usingi128=__int128_t;usingu128=__uint128_t;template<typenameT>usingV=vector<T>;template<typenameT>usingVV=vector<vector<T>>;usingvi=vector<int>;usingvl=vector<longlong>;usingvd=V<double>;usingvs=V<string>;usingvvi=vector<vector<int>>;usingvvl=vector<vector<longlong>>;template<typenameT>usingminpq=priority_queue<T,vector<T>,greater<T>>;template<typenameT,typenameU>structP:pair<T,U>{template<typename...Args>P(Args...args):pair<T,U>(args...){}usingpair<T,U>::first;usingpair<T,U>::second;P&operator+=(constP&r){first+=r.first;second+=r.second;return*this;}P&operator-=(constP&r){first-=r.first;second-=r.second;return*this;}P&operator*=(constP&r){first*=r.first;second*=r.second;return*this;}template<typenameS>P&operator*=(constS&r){first*=r,second*=r;return*this;}Poperator+(constP&r)const{returnP(*this)+=r;}Poperator-(constP&r)const{returnP(*this)-=r;}Poperator*(constP&r)const{returnP(*this)*=r;}template<typenameS>Poperator*(constS&r)const{returnP(*this)*=r;}Poperator-()const{returnP{-first,-second};}};usingpl=P<ll,ll>;usingpi=P<int,int>;usingvp=V<pl>;constexprintinf=1001001001;constexprlonglonginfLL=4004004004004004004LL;template<typenameT>intsz(constT&t){returnt.size();}template<typenameT,typenameU>inlineboolamin(T&x,Uy){return(y<x)?(x=y,true):false;}template<typenameT,typenameU>inlineboolamax(T&x,Uy){return(x<y)?(x=y,true):false;}template<typenameT>inlineTMax(constvector<T>&v){return*max_element(begin(v),end(v));}template<typenameT>inlineTMin(constvector<T>&v){return*min_element(begin(v),end(v));}template<typenameT>inlinelonglongSum(constvector<T>&v){returnaccumulate(begin(v),end(v),0LL);}template<typenameT>intlb(constvector<T>&v,constT&a){returnlower_bound(begin(v),end(v),a)-begin(v);}template<typenameT>intub(constvector<T>&v,constT&a){returnupper_bound(begin(v),end(v),a)-begin(v);}constexprlonglongTEN(intn){longlongret=1,x=10;for(;n;x*=x,n>>=1)ret*=(n&1?x:1);returnret;}template<typenameT,typenameU>pair<T,U>mkp(constT&t,constU&u){returnmake_pair(t,u);}template<typenameT>vector<T>mkrui(constvector<T>&v,boolrev=false){vector<T>ret(v.size()+1);if(rev){for(inti=int(v.size())-1;i>=0;i--)ret[i]=v[i]+ret[i+1];}else{for(inti=0;i<int(v.size());i++)ret[i+1]=ret[i]+v[i];}returnret;};template<typenameT>vector<T>mkuni(constvector<T>&v){vector<T>ret(v);sort(ret.begin(),ret.end());ret.erase(unique(ret.begin(),ret.end()),ret.end());returnret;}template<typenameF>vector<int>mkord(intN,Ff){vector<int>ord(N);iota(begin(ord),end(ord),0);sort(begin(ord),end(ord),f);returnord;}template<typenameT>vector<int>mkinv(vector<T>&v){intmax_val=*max_element(begin(v),end(v));vector<int>inv(max_val+1,-1);for(inti=0;i<(int)v.size();i++)inv[v[i]]=i;returninv;}vector<int>mkiota(intn){vector<int>ret(n);iota(begin(ret),end(ret),0);returnret;}template<typenameT>Tmkrev(constT&v){Tw{v};reverse(begin(w),end(w));returnw;}template<typenameT>boolnxp(T&v){returnnext_permutation(begin(v),end(v));}// 返り値の型は入力の T に依存// i 要素目 : [0, a[i])template<typenameT>vector<vector<T>>product(constvector<T>&a){vector<vector<T>>ret;vector<T>v;autodfs=[&](autorc,inti)->void{if(i==(int)a.size()){ret.push_back(v);return;}for(intj=0;j<a[i];j++)v.push_back(j),rc(rc,i+1),v.pop_back();};dfs(dfs,0);returnret;}// F : function(void(T&)), mod を取る操作// T : 整数型のときはオーバーフローに注意するtemplate<typenameT>TPower(Ta,longlongn,constT&I,constfunction<void(T&)>&f){Tres=I;for(;n;f(a=a*a),n>>=1){if(n&1)f(res=res*a);}returnres;}// T : 整数型のときはオーバーフローに注意するtemplate<typenameT>TPower(Ta,longlongn,constT&I=T{1}){returnPower(a,n,I,function<void(T&)>{[](T&)->void{}});}template<typenameT>TRev(constT&v){Tres=v;reverse(begin(res),end(res));returnres;}template<typenameT>vector<T>Transpose(constvector<T>&v){usingU=typenameT::value_type;if(v.empty())return{};intH=v.size(),W=v[0].size();vectorres(W,T(H,U{}));for(inti=0;i<H;i++){for(intj=0;j<W;j++){res[j][i]=v[i][j];}}returnres;}template<typenameT>vector<T>Rotate(constvector<T>&v,intclockwise=true){usingU=typenameT::value_type;intH=v.size(),W=v[0].size();vectorres(W,T(H,U{}));for(inti=0;i<H;i++){for(intj=0;j<W;j++){if(clockwise){res[W-1-j][i]=v[i][j];}else{res[j][H-1-i]=v[i][j];}}}returnres;}}// namespace Nyaan#line 58 "template/template.hpp"
// bit operation#line 1 "template/bitop.hpp"
namespaceNyaan{__attribute__((target("popcnt")))inlineintpopcnt(constu64&a){return__builtin_popcountll(a);}inlineintlsb(constu64&a){returna?__builtin_ctzll(a):64;}inlineintctz(constu64&a){returna?__builtin_ctzll(a):64;}inlineintmsb(constu64&a){returna?63-__builtin_clzll(a):-1;}template<typenameT>inlineintgbit(constT&a,inti){return(a>>i)&1;}template<typenameT>inlinevoidsbit(T&a,inti,boolb){if(gbit(a,i)!=b)a^=T(1)<<i;}constexprlonglongPW(intn){return1LL<<n;}constexprlonglongMSK(intn){return(1LL<<n)-1;}}// namespace Nyaan#line 61 "template/template.hpp"
// inout#line 1 "template/inout.hpp"
namespaceNyaan{template<typenameT,typenameU>ostream&operator<<(ostream&os,constpair<T,U>&p){os<<p.first<<" "<<p.second;returnos;}template<typenameT,typenameU>istream&operator>>(istream&is,pair<T,U>&p){is>>p.first>>p.second;returnis;}template<typenameT>ostream&operator<<(ostream&os,constvector<T>&v){ints=(int)v.size();for(inti=0;i<s;i++)os<<(i?" ":"")<<v[i];returnos;}template<typenameT>istream&operator>>(istream&is,vector<T>&v){for(auto&x:v)is>>x;returnis;}istream&operator>>(istream&is,__int128_t&x){stringS;is>>S;x=0;intflag=0;for(auto&c:S){if(c=='-'){flag=true;continue;}x*=10;x+=c-'0';}if(flag)x=-x;returnis;}istream&operator>>(istream&is,__uint128_t&x){stringS;is>>S;x=0;for(auto&c:S){x*=10;x+=c-'0';}returnis;}ostream&operator<<(ostream&os,__int128_tx){if(x==0)returnos<<0;if(x<0)os<<'-',x=-x;stringS;while(x)S.push_back('0'+x%10),x/=10;reverse(begin(S),end(S));returnos<<S;}ostream&operator<<(ostream&os,__uint128_tx){if(x==0)returnos<<0;stringS;while(x)S.push_back('0'+x%10),x/=10;reverse(begin(S),end(S));returnos<<S;}voidin(){}template<typenameT,class...U>voidin(T&t,U&...u){cin>>t;in(u...);}voidout(){cout<<"\n";}template<typenameT,class...U,charsep=' '>voidout(constT&t,constU&...u){cout<<t;if(sizeof...(u))cout<<sep;out(u...);}structIoSetupNya{IoSetupNya(){cin.tie(nullptr);ios::sync_with_stdio(false);cout<<fixed<<setprecision(15);cerr<<fixed<<setprecision(7);}}iosetupnya;}// namespace Nyaan#line 64 "template/template.hpp"
// debug#line 1 "template/debug.hpp"
namespaceDebugImpl{template<typenameU,typename=void>structis_specialize:false_type{};template<typenameU>structis_specialize<U,typenameconditional<false,typenameU::iterator,void>::type>:true_type{};template<typenameU>structis_specialize<U,typenameconditional<false,decltype(U::first),void>::type>:true_type{};template<typenameU>structis_specialize<U,enable_if_t<is_integral<U>::value,void>>:true_type{};voiddump(constchar&t){cerr<<t;}voiddump(conststring&t){cerr<<t;}voiddump(constbool&t){cerr<<(t?"true":"false");}voiddump(__int128_tt){if(t==0)cerr<<0;if(t<0)cerr<<'-',t=-t;stringS;while(t)S.push_back('0'+t%10),t/=10;reverse(begin(S),end(S));cerr<<S;}voiddump(__uint128_tt){if(t==0)cerr<<0;stringS;while(t)S.push_back('0'+t%10),t/=10;reverse(begin(S),end(S));cerr<<S;}template<typenameU,enable_if_t<!is_specialize<U>::value,nullptr_t>=nullptr>voiddump(constU&t){cerr<<t;}template<typenameT>voiddump(constT&t,enable_if_t<is_integral<T>::value>*=nullptr){stringres;if(t==Nyaan::inf)res="inf";ifconstexpr(is_signed<T>::value){if(t==-Nyaan::inf)res="-inf";}ifconstexpr(sizeof(T)==8){if(t==Nyaan::infLL)res="inf";ifconstexpr(is_signed<T>::value){if(t==-Nyaan::infLL)res="-inf";}}if(res.empty())res=to_string(t);cerr<<res;}template<typenameT,typenameU>voiddump(constpair<T,U>&);template<typenameT>voiddump(constpair<T*,int>&);template<typenameT>voiddump(constT&t,enable_if_t<!is_void<typenameT::iterator>::value>*=nullptr){cerr<<"[ ";for(autoit=t.begin();it!=t.end();){dump(*it);cerr<<(++it==t.end()?"":", ");}cerr<<" ]";}template<typenameT,typenameU>voiddump(constpair<T,U>&t){cerr<<"( ";dump(t.first);cerr<<", ";dump(t.second);cerr<<" )";}template<typenameT>voiddump(constpair<T*,int>&t){cerr<<"[ ";for(inti=0;i<t.second;i++){dump(t.first[i]);cerr<<(i==t.second-1?"":", ");}cerr<<" ]";}voidtrace(){cerr<<endl;}template<typenameHead,typename...Tail>voidtrace(Head&&head,Tail&&...tail){cerr<<" ";dump(head);if(sizeof...(tail)!=0)cerr<<",";trace(std::forward<Tail>(tail)...);}}// namespace DebugImpl#ifdef NyaanDebug
#define trc(...) \
do { \
cerr << "## " << #__VA_ARGS__ << " = "; \
DebugImpl::trace(__VA_ARGS__); \
} while (0)
#else
#define trc(...) (void(0))
#endif
#ifdef NyaanLocal
#define trc2(...) \
do { \
cerr << "## " << #__VA_ARGS__ << " = "; \
DebugImpl::trace(__VA_ARGS__); \
} while (0)
#else
#define trc2(...) (void(0))
#endif
#line 67 "template/template.hpp"
// macro#line 1 "template/macro.hpp"
#define each(x, v) for (auto&& x : v)
#define each2(x, y, v) for (auto&& [x, y] : v)
#define all(v) (v).begin(), (v).end()
#define rep(i, N) for (long long i = 0; i < (long long)(N); i++)
#define repr(i, N) for (long long i = (long long)(N)-1; i >= 0; i--)
#define rep1(i, N) for (long long i = 1; i <= (long long)(N); i++)
#define repr1(i, N) for (long long i = (N); (long long)(i) > 0; i--)
#define reg(i, a, b) for (long long i = (a); i < (b); i++)
#define regr(i, a, b) for (long long i = (b)-1; i >= (a); i--)
#define fi first
#define se second
#define ini(...) \
int __VA_ARGS__; \
in(__VA_ARGS__)
#define inl(...) \
long long __VA_ARGS__; \
in(__VA_ARGS__)
#define ins(...) \
string __VA_ARGS__; \
in(__VA_ARGS__)
#define in2(s, t) \
for (int i = 0; i < (int)s.size(); i++) { \
in(s[i], t[i]); \
}
#define in3(s, t, u) \
for (int i = 0; i < (int)s.size(); i++) { \
in(s[i], t[i], u[i]); \
}
#define in4(s, t, u, v) \
for (int i = 0; i < (int)s.size(); i++) { \
in(s[i], t[i], u[i], v[i]); \
}
#define die(...) \
do { \
Nyaan::out(__VA_ARGS__); \
return; \
} while (0)
#line 70 "template/template.hpp"
namespaceNyaan{voidsolve();}intmain(){Nyaan::solve();}#line 4 "verify/verify-yosupo-graph/yosupo-point-set-tree-path-composite-sum-fixed-root.test.cpp"
//#line 2 "modint/montgomery-modint.hpp"
template<uint32_tmod>structLazyMontgomeryModInt{usingmint=LazyMontgomeryModInt;usingi32=int32_t;usingu32=uint32_t;usingu64=uint64_t;staticconstexpru32get_r(){u32ret=mod;for(i32i=0;i<4;++i)ret*=2-mod*ret;returnret;}staticconstexpru32r=get_r();staticconstexpru32n2=-u64(mod)%mod;static_assert(mod<(1<<30),"invalid, mod >= 2 ^ 30");static_assert((mod&1)==1,"invalid, mod % 2 == 0");static_assert(r*mod==1,"this code has bugs.");u32a;constexprLazyMontgomeryModInt():a(0){}constexprLazyMontgomeryModInt(constint64_t&b):a(reduce(u64(b%mod+mod)*n2)){};staticconstexpru32reduce(constu64&b){return(b+u64(u32(b)*u32(-r))*mod)>>32;}constexprmint&operator+=(constmint&b){if(i32(a+=b.a-2*mod)<0)a+=2*mod;return*this;}constexprmint&operator-=(constmint&b){if(i32(a-=b.a)<0)a+=2*mod;return*this;}constexprmint&operator*=(constmint&b){a=reduce(u64(a)*b.a);return*this;}constexprmint&operator/=(constmint&b){*this*=b.inverse();return*this;}constexprmintoperator+(constmint&b)const{returnmint(*this)+=b;}constexprmintoperator-(constmint&b)const{returnmint(*this)-=b;}constexprmintoperator*(constmint&b)const{returnmint(*this)*=b;}constexprmintoperator/(constmint&b)const{returnmint(*this)/=b;}constexprbooloperator==(constmint&b)const{return(a>=mod?a-mod:a)==(b.a>=mod?b.a-mod:b.a);}constexprbooloperator!=(constmint&b)const{return(a>=mod?a-mod:a)!=(b.a>=mod?b.a-mod:b.a);}constexprmintoperator-()const{returnmint()-mint(*this);}constexprmintoperator+()const{returnmint(*this);}constexprmintpow(u64n)const{mintret(1),mul(*this);while(n>0){if(n&1)ret*=mul;mul*=mul;n>>=1;}returnret;}constexprmintinverse()const{intx=get(),y=mod,u=1,v=0,t=0,tmp=0;while(y>0){t=x/y;x-=t*y,u-=t*v;tmp=x,x=y,y=tmp;tmp=u,u=v,v=tmp;}returnmint{u};}friendostream&operator<<(ostream&os,constmint&b){returnos<<b.get();}friendistream&operator>>(istream&is,mint&b){int64_tt;is>>t;b=LazyMontgomeryModInt<mod>(t);return(is);}constexpru32get()const{u32ret=reduce(a);returnret>=mod?ret-mod:ret;}staticconstexpru32get_mod(){returnmod;}};#line 6 "verify/verify-yosupo-graph/yosupo-point-set-tree-path-composite-sum-fixed-root.test.cpp"
//#line 2 "tree/static-top-tree-vertex-based.hpp"
#line 6 "tree/static-top-tree-vertex-based.hpp"
usingnamespacestd;#line 2 "tree/convert-tree.hpp"
#line 2 "graph/graph-template.hpp"
template<typenameT>structedge{intsrc,to;Tcost;edge(int_to,T_cost):src(-1),to(_to),cost(_cost){}edge(int_src,int_to,T_cost):src(_src),to(_to),cost(_cost){}edge&operator=(constint&x){to=x;return*this;}operatorint()const{returnto;}};template<typenameT>usingEdges=vector<edge<T>>;template<typenameT>usingWeightedGraph=vector<Edges<T>>;usingUnweightedGraph=vector<vector<int>>;// Input of (Unweighted) GraphUnweightedGraphgraph(intN,intM=-1,boolis_directed=false,boolis_1origin=true){UnweightedGraphg(N);if(M==-1)M=N-1;for(int_=0;_<M;_++){intx,y;cin>>x>>y;if(is_1origin)x--,y--;g[x].push_back(y);if(!is_directed)g[y].push_back(x);}returng;}// Input of Weighted Graphtemplate<typenameT>WeightedGraph<T>wgraph(intN,intM=-1,boolis_directed=false,boolis_1origin=true){WeightedGraph<T>g(N);if(M==-1)M=N-1;for(int_=0;_<M;_++){intx,y;cin>>x>>y;Tc;cin>>c;if(is_1origin)x--,y--;g[x].emplace_back(x,y,c);if(!is_directed)g[y].emplace_back(y,x,c);}returng;}// Input of Edgestemplate<typenameT>Edges<T>esgraph([[maybe_unused]]intN,intM,intis_weighted=true,boolis_1origin=true){Edges<T>es;for(int_=0;_<M;_++){intx,y;cin>>x>>y;Tc;if(is_weighted)cin>>c;elsec=1;if(is_1origin)x--,y--;es.emplace_back(x,y,c);}returnes;}// Input of Adjacency Matrixtemplate<typenameT>vector<vector<T>>adjgraph(intN,intM,TINF,intis_weighted=true,boolis_directed=false,boolis_1origin=true){vector<vector<T>>d(N,vector<T>(N,INF));for(int_=0;_<M;_++){intx,y;cin>>x>>y;Tc;if(is_weighted)cin>>c;elsec=1;if(is_1origin)x--,y--;d[x][y]=c;if(!is_directed)d[y][x]=c;}returnd;}/**
* @brief グラフテンプレート
* @docs docs/graph/graph-template.md
*/#line 4 "tree/convert-tree.hpp"
template<typenameT>structhas_cost{private:template<typenameU>staticautoconfirm(Uu)->decltype(u.cost,std::true_type());staticautoconfirm(...)->std::false_type;public:enum:bool{value=decltype(confirm(std::declval<T>()))::value};};template<typenameT>vector<vector<T>>inverse_tree(constvector<vector<T>>&g){intN=(int)g.size();vector<vector<T>>rg(N);for(inti=0;i<N;i++){for(auto&e:g[i]){ifconstexpr(is_same<T,int>::value){rg[e].push_back(i);}elseifconstexpr(has_cost<T>::value){rg[e].emplace_back(e.to,i,e.cost);}else{assert(0);}}}returnrg;}template<typenameT>vector<vector<T>>rooted_tree(constvector<vector<T>>&g,introot=0){intN=(int)g.size();vector<vector<T>>rg(N);vector<char>v(N,false);v[root]=true;queue<int>que;que.emplace(root);while(!que.empty()){autop=que.front();que.pop();for(auto&e:g[p]){if(v[e]==false){v[e]=true;que.push(e);rg[p].push_back(e);}}}returnrg;}/**
* @brief 根付き木・逆辺からなる木への変換
*/#line 2 "tree/heavy-light-decomposition.hpp"
#line 4 "tree/heavy-light-decomposition.hpp"
template<typenameG>structHeavyLightDecomposition{private:voiddfs_sz(intcur){size[cur]=1;for(auto&dst:g[cur]){if(dst==par[cur]){if(g[cur].size()>=2&&int(dst)==int(g[cur][0]))swap(g[cur][0],g[cur][1]);elsecontinue;}depth[dst]=depth[cur]+1;par[dst]=cur;dfs_sz(dst);size[cur]+=size[dst];if(size[dst]>size[g[cur][0]]){swap(dst,g[cur][0]);}}}voiddfs_hld(intcur){down[cur]=id++;for(autodst:g[cur]){if(dst==par[cur])continue;nxt[dst]=(int(dst)==int(g[cur][0])?nxt[cur]:int(dst));dfs_hld(dst);}up[cur]=id;}// [u, v)vector<pair<int,int>>ascend(intu,intv)const{vector<pair<int,int>>res;while(nxt[u]!=nxt[v]){res.emplace_back(down[u],down[nxt[u]]);u=par[nxt[u]];}if(u!=v)res.emplace_back(down[u],down[v]+1);returnres;}// (u, v]vector<pair<int,int>>descend(intu,intv)const{if(u==v)return{};if(nxt[u]==nxt[v])return{{down[u]+1,down[v]}};autores=descend(u,par[nxt[v]]);res.emplace_back(down[nxt[v]],down[v]);returnres;}public:G&g;introot,id;vector<int>size,depth,down,up,nxt,par;HeavyLightDecomposition(G&_g,int_root=0):g(_g),root(_root),id(0),size(g.size(),0),depth(g.size(),0),down(g.size(),-1),up(g.size(),-1),nxt(g.size(),root),par(g.size(),root){dfs_sz(root);dfs_hld(root);}pair<int,int>idx(inti)const{returnmake_pair(down[i],up[i]);}template<typenameF>voidpath_query(intu,intv,boolvertex,constF&f){intl=lca(u,v);for(auto&&[a,b]:ascend(u,l)){ints=a+1,t=b;s>t?f(t,s):f(s,t);}if(vertex)f(down[l],down[l]+1);for(auto&&[a,b]:descend(l,v)){ints=a,t=b+1;s>t?f(t,s):f(s,t);}}template<typenameF>voidpath_noncommutative_query(intu,intv,boolvertex,constF&f){intl=lca(u,v);for(auto&&[a,b]:ascend(u,l))f(a+1,b);if(vertex)f(down[l],down[l]+1);for(auto&&[a,b]:descend(l,v))f(a,b+1);}template<typenameF>voidsubtree_query(intu,boolvertex,constF&f){f(down[u]+int(!vertex),up[u]);}intlca(inta,intb){while(nxt[a]!=nxt[b]){if(down[a]<down[b])swap(a,b);a=par[nxt[a]];}returndepth[a]<depth[b]?a:b;}intdist(inta,intb){returndepth[a]+depth[b]-depth[lca(a,b)]*2;}};/**
* @brief Heavy Light Decomposition(重軽分解)
* @docs docs/tree/heavy-light-decomposition.md
*/#line 10 "tree/static-top-tree-vertex-based.hpp"
namespaceStaticTopTreeVertexBasedImpl{enumType{Vertex,Compress,Rake,Add_Edge,Add_Vertex};template<typenameG>structStaticTopTreeVertexBased{constHeavyLightDecomposition<G>&hld;vector<vector<int>>g;introot;// 元の木の rootinttt_root;// top tree の rootvector<int>P,L,R;vector<Type>T;StaticTopTreeVertexBased(constHeavyLightDecomposition<G>&_hld):hld(_hld){root=hld.root;g=rooted_tree(hld.g,root);intn=g.size();P.resize(n,-1),L.resize(n,-1),R.resize(n,-1);T.resize(n,Type::Vertex);build();}private:intadd(intl,intr,Typet){if(t==Type::Compressort==Type::Rake){assert(l!=-1andr!=-1);}if(t==Type::Add_Edge){assert(l!=-1andr==-1);}assert(t!=Type::Vertexandt!=Type::Add_Vertex);intk=P.size();P.push_back(-1),L.push_back(l),R.push_back(r),T.push_back(t);if(l!=-1)P[l]=k;if(r!=-1)P[r]=k;returnk;}intadd2(intk,intl,intr,Typet){assert(k<(int)g.size());assert(t==Type::Vertexort==Type::Add_Vertex);if(t==Type::Vertex){assert(l==-1andr==-1);}else{assert(l!=-1andr==-1);}P[k]=-1,L[k]=l,R[k]=r,T[k]=t;if(l!=-1)P[l]=k;if(r!=-1)P[r]=k;returnk;}pair<int,int>merge(constvector<pair<int,int>>&a,Typet){assert(!a.empty());if(a.size()==1)returna[0];intsum_s=0;for(auto&[_,s]:a)sum_s+=s;vector<pair<int,int>>b,c;for(auto&[i,s]:a){(sum_s>s?b:c).emplace_back(i,s);sum_s-=s*2;}auto[i,si]=merge(b,t);auto[j,sj]=merge(c,t);return{add(i,j,t),si+sj};}pair<int,int>compress(inti){vector<pair<int,int>>chs;while(true){chs.push_back(add_vertex(i));if(g[i].empty())break;i=g[i][0];}returnmerge(chs,Type::Compress);}pair<int,int>rake(inti){vector<pair<int,int>>chs;for(intj=1;j<(int)g[i].size();j++)chs.push_back(add_edge(g[i][j]));if(chs.empty())return{-1,0};returnmerge(chs,Type::Rake);}pair<int,int>add_edge(inti){auto[j,sj]=compress(i);return{add(j,-1,Type::Add_Edge),sj};}pair<int,int>add_vertex(inti){auto[j,sj]=rake(i);return{add2(i,j,-1,j==-1?Type::Vertex:Type::Add_Vertex),sj+1};}voidbuild(){auto[i,n]=compress(root);assert((int)g.size()==n);tt_root=i;}};template<typenameG,typenamePath,typenamePoint,typenameVertex,typenameCompress,typenameRake,typenameAdd_edge,typenameAdd_vertex>structDPonStaticTopTreeVertexBased{constStaticTopTreeVertexBased<G>tt;vector<Path>path;vector<Point>point;constVertexvertex;constCompresscompress;constRakerake;constAdd_edgeadd_edge;constAdd_vertexadd_vertex;DPonStaticTopTreeVertexBased(constHeavyLightDecomposition<G>&hld,constVertex&_vertex,constCompress&_compress,constRake&_rake,constAdd_edge&_add_edge,constAdd_vertex&_add_vertex):tt(hld),vertex(_vertex),compress(_compress),rake(_rake),add_edge(_add_edge),add_vertex(_add_vertex){intn=tt.P.size();path.resize(n),point.resize(n);dfs(tt.tt_root);}Pathget(){returnpath[tt.tt_root];}voidupdate(intk){while(k!=-1)_update(k),k=tt.P[k];}private:void_update(intk){if(tt.T[k]==Type::Vertex){path[k]=vertex(k);}elseif(tt.T[k]==Type::Compress){path[k]=compress(path[tt.L[k]],path[tt.R[k]]);}elseif(tt.T[k]==Type::Rake){point[k]=rake(point[tt.L[k]],point[tt.R[k]]);}elseif(tt.T[k]==Type::Add_Edge){point[k]=add_edge(path[tt.L[k]]);}else{path[k]=add_vertex(point[tt.L[k]],k);}}voiddfs(intk){if(tt.L[k]!=-1)dfs(tt.L[k]);if(tt.R[k]!=-1)dfs(tt.R[k]);_update(k);}};}// namespace StaticTopTreeVertexBasedImplusingStaticTopTreeVertexBasedImpl::DPonStaticTopTreeVertexBased;usingStaticTopTreeVertexBasedImpl::StaticTopTreeVertexBased;/*
// template
using Path = ;
using Point = ;
auto vertex = [&](int i) -> Path {
};
auto compress = [&](const Path& p, const Path& c) -> Path {
};
auto rake = [&](const Point& a, const Point& b) -> Point {
};
auto add_edge = [&](const Path& a) -> Point {
};
auto add_vertex = [&](const Point& a, int i) -> Path {
};
HeavyLightDecomposition hld{g};
DPonStaticTopTreeVertexBased<vector<vector<int>>, Path, Point,
decltype(vertex), decltype(compress), decltype(rake), decltype(add_edge),
decltype(add_vertex)>
dp(hld, vertex, compress, rake, add_edge, add_vertex);
*//**
* @brief Static Top Tree
*/#line 8 "verify/verify-yosupo-graph/yosupo-point-set-tree-path-composite-sum-fixed-root.test.cpp"
//usingmint=LazyMontgomeryModInt<998244353>;structPath{minta,b,s,x;};structPoint{mints,x;};vector<mint>calc(intN,intQ,vector<int>A,vector<int>U,vector<int>V,vector<int>B,vector<int>C,vector<int>cmd,vector<int>I,vector<int>X,vector<int>Y){vector<vector<int>>g(2*N-1);rep(e,N-1){g[N+e].push_back(U[e]),g[U[e]].push_back(N+e);g[N+e].push_back(V[e]),g[V[e]].push_back(N+e);}autovertex=[&](inti)->Path{if(i<N)return{1,0,A[i],1};inte=i-N;return{B[e],C[e],0,0};};autocompress=[&](constPath&p,constPath&c)->Path{return{p.a*c.a,p.a*c.b+p.b,p.s+p.a*c.s+p.b*c.x,p.x+c.x};};autorake=[&](constPoint&a,constPoint&b)->Point{return{a.s+b.s,a.x+b.x};};autoadd_edge=[&](constPath&a)->Point{return{a.s,a.x};};autoadd_vertex=[&](constPoint&a,inti)->Path{if(i<N)return{1,0,a.s+A[i],a.x+1};inte=i-N;return{B[e],C[e],a.s*B[e]+a.x*C[e],a.x};};HeavyLightDecompositionhld{g};DPonStaticTopTreeVertexBased<vector<vector<int>>,Path,Point,decltype(vertex),decltype(compress),decltype(rake),decltype(add_edge),decltype(add_vertex)>dp(hld,vertex,compress,rake,add_edge,add_vertex);vector<mint>ans(Q);rep(q,Q){if(cmd[q]==0){A[I[q]]=X[q];dp.update(I[q]);}else{B[I[q]]=X[q],C[I[q]]=Y[q];dp.update(N+I[q]);}ans[q]=dp.get().s;}returnans;}voidNyaan::solve(){intN,Q;in(N,Q);vector<int>A(N);for(auto&x:A)in(x);vector<int>U(N-1),V(N-1),B(N-1),C(N-1);rep(i,N-1)in(U[i],V[i],B[i],C[i]);vector<int>cmd(Q),I(Q),X(Q),Y(Q,-1);rep(i,Q){in(cmd[i],I[i],X[i]);if(cmd[i]==1)in(Y[i]);}autoac=calc(N,Q,A,U,V,B,C,cmd,I,X,Y);for(auto&x:ac)out(x.get());}