��վ�ܷ�������

树链剖分板子(联合线段树)

插入一些有利于理解的图片

有关dfn序

关于寻找路径不断往上翻

// luogu-judger-enable-o2
#include <bits/stdc++.h>
#define lson 2*p
#define rson 2*p+1
#define N 200000+110
using namespace std;
int n,m,head[N],sizee[N],deep[N],fa[N],son[N],top[N],w[N],mod,root,cnt,wt[N],id[N],tot;
//------------------------线段树---------------------------------------
struct tree
{
    int left;
    int right;
    int val;
    int sum;
}a[4*N];

void pushup(int p)
{
     a[p].sum=a[2*p].sum+a[2*p+1].sum;
     a[p].sum%=mod;
}
void build(int p,int l,int r)
{
    a[p].left=l;
    a[p].right=r;
    //a[p].val=-1;
    if(l==r){a[p].sum=wt[l];a[p].sum%=mod;return;}
    int mid=(l+r)/2;
    build(2*p,l,mid);
    build(2*p+1,mid+1,r);
    pushup(p);
}
void pushdown(int p)
{
     if(a[p].val!=0)
    {
        a[2*p].val+=a[p].val;
        a[2*p].sum+=(a[2*p].right-a[2*p].left+1)*a[p].val;
        a[2*p+1].val+=a[p].val;
        a[2*p+1].sum+=(a[2*p+1].right-a[2*p+1].left+1)*a[p].val;
        a[lson].sum%=mod;
        a[rson].sum%=mod;
        a[p].val=0;
    }
}

void update(int p,int l,int r,int d)
{
    if(a[p].left==l&&a[p].right==r){a[p].val+=d;a[p].sum+=(r-l+1)*d;a[p].sum%=mod;return;}
    pushdown(p);
    int mid=(a[p].left+a[p].right)/2;
    if(r<=mid)update(2*p,l,r,d);
    else if(l>mid)update(2*p+1,l,r,d);
         else {update(2*p,l,mid,d);update(2*p+1,mid+1,r,d);}
   pushup(p);
}
int  query(int p,int l,int r)
{
    if(a[p].left==l&&a[p].right==r)return a[p].sum%mod;
   pushdown(p);
    int mid=(a[p].left+a[p].right)/2;
    if(r<=mid)return query(2*p,l,r);
    else if(l>mid)return query(2*p+1,l,r);
         else return query(2*p,l,mid)+query(2*p+1,mid+1,r);
}

//---------------------树链剖分----------------------------------------
struct node
{
    int nt=-1,to;
}edge[N];
void add(int x,int y)
{
    cnt++;
    edge[cnt].to=y;
    edge[cnt].nt=head[x];
    head[x]=cnt;
}
void dfs(int x,int fa1,int d)
{
    deep[x]=d;fa[x]=fa1;sizee[x]=1;
    for(int i=head[x];i!=-1;i=edge[i].nt)
    {
        int v=edge[i].to;
        if(v==fa1)continue;
        dfs(v,x,d+1);
        sizee[x]+=sizee[v];
        if(sizee[v]>sizee[son[x]])son[x]=v;//标记重边
    }
}
void dfs2(int x,int tp)//初始化每个节点所在重边的最小节点,最上面的节点(没有儿子定为自己
{
    id[x]=++tot;
    wt[tot]=w[x];
    top[x]=tp;
    if(!son[x])return;
    dfs2(son[x],tp);
    for(int i=head[x];i!=-1;i=edge[i].nt)
    {
        int v=edge[i].to;
        if(v!=fa[x]&&v!=son[x])
            dfs2(v,v);
    }
}

void uprange(int x,int y,int k)
{
    k%=mod;
    while(top[x]!=top[y])
    {
        if(deep[top[x]]<deep[top[y]])swap(x,y);
        update(1,id[top[x]],id[x],k);
        x=fa[top[x]];
    }
    if(deep[x]>deep[y])swap(x,y);
    update(1,id[x],id[y],k);
}
int  qrange(int x,int y)
{
    int ans=0;
    while(top[x]!=top[y])
    {
        if(deep[top[x]]<deep[top[y]])swap(x,y);
        ans+=query(1,id[top[x]],id[x]);
        ans%=mod;
        x=fa[top[x]];
    }
    if(deep[x]>deep[y])swap(x,y);
    ans+=query(1,id[x],id[y]);
    return ans%mod;



}
void upson(int x,int k)
{
    k%=mod;
    update(1,id[x],id[x]+sizee[x]-1,k);
}
int qson(int x)
{
    return query(1,id[x],id[x]+sizee[x]-1)%mod;
}
//------------------------主函数---------------------------------------
int main()
{
    memset(head,-1,sizeof(head));
    scanf("%d%d%d%d",&n,&m,&root,&mod);
    for(int i=1;i<=n;i++)scanf("%d",&w[i]);
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
    }
    dfs(root,0,1);
    dfs2(root,root);
    build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        int op,x,y,z;
        scanf("%d",&op);
        switch(op)
        {
            case 1:scanf("%d%d%d",&x,&y,&z);uprange(x,y,z);break;
            case 2:scanf("%d%d",&x,&y);printf("%d\n",qrange(x,y));break;
            case 3:scanf("%d%d",&x,&z);
                    upson(x,z);
                    break;
            case 4:scanf("%d",&x);printf("%d\n",qson(x));break;
        }
    }




    return 0;
}