树链剖分

//树链剖分 
//将最短路上的区间分成若干条链 使得每一条链上的操作都能用线段树解决 
//分轻重 重儿子为某节点的儿子中儿子最多的子节点 重儿子之间的边为重边 重边相连即为重链 
#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;
}
原文地址:https://www.cnblogs.com/water-radish/p/9280657.html