//树链剖分 //将最短路上的区间分成若干条链 使得每一条链上的操作都能用线段树解决 //分轻重 重儿子为某节点的儿子中儿子最多的子节点 重儿子之间的边为重边 重边相连即为重链 #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> using namespace std; int n,m,root,MOD,dot[100001],dotid[100001];//dot[]儿子编号 dotid[]儿子新编号 int cnt,head[100001]; struct uio{ int to,next; }edge[200001];//链式前向星 int seg[400001],lazy[400001];//线段树数组 int hvyson[100001],id[100001],fa[100001],dfsno,dep[100001],siz[100001],top[100001]; //hvyson[]重儿子编号 id[]新编号(新dfs序) fa[]父节点 dfsno为dfs序 dep[]深度 siz[]子树大小 top[]当前链顶端节点 void add(int x,int y) { edge[++cnt].next=head[x]; edge[cnt].to=y; head[x]=cnt; } //--------------------------------------------------以下为线段树 void pushdown(int now,int len) { int lson=now<<1,rson=(now<<1)|1; lazy[lson]+=lazy[now]; lazy[rson]+=lazy[now]; seg[lson]+=lazy[now]*(len-(len>>1)); seg[rson]+=lazy[now]*(len>>1); seg[lson]%=MOD; seg[rson]%=MOD; lazy[now]=0; } void build(int now,int l,int r) { if(l==r) { seg[now]=dotid[l]; seg[now]%=MOD; return; } int mid=(l+r)/2; build(now<<1,l,mid); build((now<<1)|1,mid+1,r); seg[now]=(seg[now<<1]+seg[(now<<1)|1])%MOD; } int query(int now,int l,int r,int L,int R) { int ans=0; if(L<=l&&r<=R) { ans+=seg[now]; return ans%MOD; } pushdown(now,r-l+1); int mid=(l+r)/2; if(L<=mid) ans+=query(now<<1,l,mid,L,R); if(mid+1<=R) ans+=query((now<<1)|1,mid+1,r,L,R); return ans%MOD; } void add_update(int now,int l,int r,int L,int R,int z) { if(L<=l&&r<=R) { lazy[now]+=z; seg[now]+=z*(r-l+1); return; } pushdown(now,r-l+1); int mid=(l+r)/2; if(L<=mid) add_update(now<<1,l,mid,L,R,z); if(mid+1<=R) add_update((now<<1)|1,mid+1,r,L,R,z); seg[now]=(seg[now<<1]+seg[(now<<1)|1])%MOD; } //--------------------------------------------------以上为线段树 void dfs1(int x,int f,int depth)//求普通dfs序 目的计算子树大小 { dep[x]=depth;//标记深度 fa[x]=f;//标记父亲 siz[x]=1;//标记子树大小 int maxson=-1;//记录重儿子的儿子数 for(int i=head[x];i;i=edge[i].next) { int y=edge[i].to; if(y==f) continue; dfs1(y,x,depth+1); siz[x]+=siz[y];//把它的儿子的儿子数加到它身上 if(siz[y]>maxson) { hvyson[x]=y;//标记重儿子 maxson=siz[y]; } } } void dfs2(int x,int topf)//topf为当前重链的最顶端节点 { id[x]=++dfsno;//标记每个点的新编号(新dfs序) dotid[dfsno]=dot[x];//把点值赋值到新编号上 top[x]=topf;//标记重链顶端 if(!hvyson[x])//没有重儿子就返回 return; dfs2(hvyson[x],topf);//优先dfs重儿子 for(int i=head[x];i;i=edge[i].next) { int y=edge[i].to; if(y==fa[x]||y==hvyson[x]) continue; dfs2(y,y);//对于每个轻儿子其重链顶端都为自己 } } void update_section(int x,int y,int z) { z%=MOD; while(top[x]!=top[y])//若两个点不在同一条重链上 { if(dep[top[x]]<dep[top[y]])//将x点改为所在重链顶端深度更深的那个点 swap(x,y); add_update(1,1,n,id[top[x]],id[x],z);//先修改从x到x所在重链顶端的点的权值 x=fa[top[x]];//把x跳到x所在重链顶端的那个点 } //直至两点位于同一条重链上 if(dep[x]>dep[y])//把x改为深度更浅的那个点 swap(x,y); add_update(1,1,n,id[x],id[y],z); } int query_section(int x,int y)//注释全部同上 { int ans=0; while(top[x]!=top[y]) { if(dep[top[x]]<dep[top[y]]) swap(x,y); ans+=query(1,1,n,id[top[x]],id[x]); ans%=MOD; x=fa[top[x]]; } if(dep[x]>dep[y]) swap(x,y); ans+=query(1,1,n,id[x],id[y]); return ans%MOD; } void update_son(int x,int z)//直接用线段树处理 { add_update(1,1,n,id[x],id[x]+siz[x]-1,z); } int query_son(int x)//同上 { return query(1,1,n,id[x],id[x]+siz[x]-1); } void do_something() { for(int i=1;i<=m;i++) { int k,u,v,w; scanf("%d",&k); if(k==1) { scanf("%d%d%d",&u,&v,&w); update_section(u,v,w); } if(k==2) { scanf("%d%d",&u,&v); printf("%d ",query_section(u,v)); } if(k==3) { scanf("%d%d",&u,&v); update_son(u,v); } if(k==4) { scanf("%d",&u); printf("%d ",query_son(u)); } } } int main() { scanf("%d%d%d%d",&n,&m,&root,&MOD); for(int i=1;i<=n;i++) scanf("%d",&dot[i]); for(int i=1;i<n;i++) { int u,v; scanf("%d%d",&u,&v); add(u,v); add(v,u); } dfs1(root,0,1); dfs2(root,root); build(1,1,n); do_something(); return 0; }