树剖

#include
#include
#define MAXN 1e7+5
using namespace std;
int hd[MAXN];
struct node{
    int frm,to,nxt;
}edge[MAXN];
int cnt=0; 
void add(int u,int v)
{
    cnt++;
    edge[cnt].to=v;
    edge[cnt].frm=u;
    edge[cnt].nxt=hd[u];
    hd[u]=cnt;
}
void dfs1(int u,int f,int dep)
{
    deep[u]=dep;
    fa[u]=f;
    siz[u]=1;
    int maxson=-1;
    for(int i=hd[u];i;i=edge[i].nxt )
    {
        int v=edge[i].to;
        if(v==f)continue;
        dfs1(v,u,dep+1);
        siz[u]+=siz[v];
        if(siz[v]>maxson)
            maxson=siz[v],son[u]=v;
    }
}
int cnt=0;
//把重儿子找出来,开始按顺序放到线段树排列中,
//还要记录top,重链上的top 
dfs2(int u,int topf)
{
    idx[u]=++cnt;
    a[cnt]=b[u];//点权
    top[u]=topf;
    if(!son[u])return;
    dfs2(son[u],topf);
    for(int i=hd[u];i;i=edge[i].nxt)
    {
        int v=edge[i].to;
        if(v==fa[u] || v==son[u])continue;/
        dfs2(v,v);
    } 
}
void pushup(int rt)
{
    tre[rt].w=(tre[rt<<1].w+tre[rt<<1|1].w+MOD)%MOD;
}
void build(int rt,int L,int R)
{
    tre[rt].l=L;
    tre[rt].r=R;
    tre[rt].siz=R-L+1;
    if(L==R)
    {
        tre[rt].w=a[L];
        return;
    }
    int mid=(L+R)>>1;
    build(rt<<1,L,mid);
    build(rt<<1|1,mid+1,R);
    pushup(rt);
}
void pushdown(int rt)
{
    if(tre[rt].f) 
    {
        tre[rt<<1].w+=(tre[rt<<1].w+(tre[rt<<1].siz*tre[rt].f)%MOD)%MOD;
        tre[rt<<1|1].w+=(tre[rt<<1|1].w+(tre[rt<<1|1].siz*tre[rt].f)%MOD)%MOD;
        tre[rt<<1].f=(tre[rt<<1].f+tre[rt].f)%MOD; 
        tre[rt<<1|1].f=(tre[rt<<1|1].f+tre[rt<<1|1].f)%MOD;
        tre[rt<<1].f=0;
    }
}
void SegmentAdd(int rt,int L,int R,int val)
{
    if(L<=tre[rt].l && R>=tre[rt].r)
    {
        tre[rt].w+=tre[rt].siz*val;
        tre[rt].f+val;
        return;
    }
    pushdown(rt);
    int mid=(tre[rt].l+tre[rt].r)>>1;
    if(L<=mid) SegmentAdd(rt<<1,L,R,val);
    if(R>mid) SegmentAdd(rt<<1|1,L,R,val);
}
void TreeAdd(int x,int y,int val)//两点之间add一个数 
{
    while(top[x]!=top[y])//让x深 
    {
        if(dep[top[x]]1,id[top[x]],idx[x],val);
        x=fa[top[x]];
    }
    if(deep[x]>deep[y]) swap(x,y);
    SegmentAdd(1,idx[x],idx[y],val);//让x浅 
}
int SegmentSum(int rt,int L,int R)
{
    int ans=0;
    if(L<=tre[rt].l && R>=tre[rt].R)
        return tre[rt].w;
    pushdown(rt);
    int mid=(tre[rt].l+tre[rt].r)>>1;
    if(L<=mid) 
        ans=(ans+SegmentSum(rt<<1,L,R))%MOD;
    if(R>mid)
        ans=(ans+SegmentSum(rt<<1|1,L,R))%MOD;
    return ans;
} 
void TreeSum(int x,int y)//求两点之间的和 
{
    int ans=0;
    while(top[x]!=top[y])
    {
        if(deep[top[x]]//将x变深 
        ans=(ans+SegmentSum(1,idx[top[x]],idx[x])%MOD)%MOD;
        x=fa[top[x]];
    }
    if(deep[x]>deep[y]) swap(x,y);
    ans=(ans+Segment(1,idx[x],idx[y])%MOD)%MOD;
    printf("%d\n",ans);
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&rt,&MOD);
    for(int i=1;i<=N;i++)
        scanf("%d",&b[i]);
    int x,y,z;
    for(int i=1;i<=N;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
    }
    dfs1(rt,0,1);
    dfs2(rt,rt);
    build(1,1,n);
    while(m--)
    {
        int opt,x,y,z;
        if(opt==1)
        {
            scanf("%d%d%d",&x,&y,&z);z%=MOD;
            TreeAdd(x,y,z);
        }
        else if(opt==2)
        {
            scanf("%d%d",&x,&y);
            TreeSum(x,y);//x到y的和    
        } 
        else if(opt==3)
        {
            scanf("%d%d",&x,&y);
            SegmentAdd(1,idx[x],idx[x]+size[x]-1,z%MOD);
        }
        else if(opt==4)
        {
            scanf("%d",&x);
            printf("%d\n",SegmentSum(1,idx[x],idx[i]+size[x]-1)%MOD) ;
        }
    }
    return 0;
}

你可能感兴趣的:(树剖)