树链剖分

Posted faced

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树链剖分相关的知识,希望对你有一定的参考价值。

树链剖分https://www.luogu.org/problemnew/show/P3384

概念

树链剖分,是一种将树剖分成多条不相交的链的算法,并通过其他的数据结构来维护这些链上的信息。
最简单的例子就是LCA,假设现在有一棵退化成链的树。如果要求任意两点的LCA,因为他们在同一条链上的缘故,只需要判断一下两者的深度就行了。由此可见,在链上是比在树上更好操作的。
那么该怎么将一棵树剖分开来捏?
先搬出一堆概念:

重儿子
在以X节点为根的子树中,节点数最多的子树的根节点,即是X节点的重儿子。
重边
连接X节点与X节点的重儿子的边,我们叫他重边。
重链
一堆重边连起来的链。
轻链
一堆非重边连起来的链。

对于每个节点,找出其重儿子,就可以剖分成一条条重链与轻链。

实现

数组定义

val[N]每个节点的初值
size[N]每个节点子树的大小
son[N]每个节点的重儿子
fa[N]每个节点的父亲
w[N]每个节点在线段树上的编号
rank[N]线段树上节点在树中的编号
dep[N]节点深度
top[N]每个点所在链的链顶

dfs

第一遍dfs处理出size[],fa[],son[],dep[]
第二遍dfs处理出top[],w[],rank[]

线段树

用线段树维护树链,并实现链上的操作,常见操作如下:
将树从X到Y结点最短路径上所有节点的值都加上Z
求树从X到Y结点最短路径上所有节点的值之和
将以X为根节点的子树内所有节点值都加上Z
求以X为根节点的子树内所有节点值之和
对于操作3:我们知道一颗子树内的编号一定是连续的,那么以X节点为根的子树的区间就是{w[x],w[x]+size[x]-1}

#define RG register
#define lson root<<1
#define rson root<<1|1
#define mid ((l+r)>>1)
#include<cstdio>
#include<iostream>
using namespace std;
const int N=1e5+5;
inline int read()
{
    RG int x=0,w=1;RG char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)w=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘)x=x*10+ch-‘0‘,ch=getchar();
    return x*w;
}
int n,m,r,p,cnt,ct;
int lazy[N<<2],sum[N<<2];
int val[N],size[N],son[N],fa[N],w[N],rank[N],last[N],dep[N],top[N];
struct edge{
    int to,next,w;
}e[N<<1];
void insert(int u,int v)
{
    e[++cnt]=(edge){v,last[u]};last[u]=cnt;
    e[++cnt]=(edge){u,last[v]};last[v]=cnt;
}
inline void dfs1(int now)
{
    size[now]=1;
    for(int i=last[now];i;i=e[i].next)
    {
        int v=e[i].to;
        if(v!=fa[now])
        {
            fa[v]=now;
            dep[v]=dep[now]+1;
            dfs1(v);
            size[now]+=size[v];
            if(size[v]>size[son[now]])son[now]=v;
        }
    }
}
inline void dfs2(int now,int tp)
{
    top[now]=tp;w[now]=++ct;rank[ct]=now;
    if(son[now])dfs2(son[now],tp);//重儿子
    for(int i=last[now];i;i=e[i].next)
    {
        int v=e[i].to;
        if(v==son[now]||v==fa[now])continue;
        dfs2(v,v);//轻儿子的top就是本身
    }
}
inline void Pushup(int root)
{
    sum[root]=(sum[lson]+sum[rson])%p;
}
inline void Build(int root,int l,int r)
{
    lazy[root]=0;
    if(l==r)
    {
        sum[root]=val[rank[l]];
        return;
    }
    Build(lson,l,mid);
    Build(rson,mid+1,r);
    Pushup(root);
}
inline void Pushdown(int root,int l,int r)
{
    if(lazy[root])
    {
        lazy[lson]=(lazy[lson]+lazy[root])%p;
        lazy[rson]=(lazy[rson]+lazy[root])%p;
        sum[lson]=(sum[lson]+lazy[root]*(mid-l+1)%p)%p;
        sum[rson]=(sum[rson]+lazy[root]*(r-mid)%p)%p;
        lazy[root]=0;
    }
}
inline void Modify(int root,int l,int r,int ll,int rr,int k)
{
    if(ll<=l&&r<=rr)
    {
        lazy[root]=(lazy[root]+k)%p;
        sum[root]=(sum[root]+k*(r-l+1)%p)%p;
        return;
    }
    Pushdown(root,l,r);
    if(ll<=mid)Modify(lson,l,mid,ll,rr,k);
    if(rr>mid)Modify(rson,mid+1,r,ll,rr,k);
    Pushup(root);
}
inline int Query(int root,int l,int r,int ll,int rr)
{
    if(ll<=l&&r<=rr)return sum[root];
    int S=0;
    Pushdown(root,l,r);
    if(ll<=mid)S=(S+Query(lson,l,mid,ll,rr))%p;
    if(mid<rr)S=(S+Query(rson,mid+1,r,ll,rr))%p;
    Pushup(root);
    return S;
}
inline void Modify_Tree(int x,int y,int k)
{
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        Modify(1,1,n,w[top[x]],w[x],k);
        x=fa[top[x]];
    }
    if(dep[x]>dep[y])swap(x,y);
    Modify(1,1,n,w[x],w[y],k);
}
inline int Query_Tree(int x,int y)
{
    int Sum=0;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        Sum=(Sum+Query(1,1,n,w[top[x]],w[x]))%p;
        x=fa[top[x]];
    }
    if(dep[x]>dep[y])swap(x,y);
    return (Sum+Query(1,1,n,w[x],w[y]))%p;
}
inline void Modify_Son(int x,int k)
{
    Modify(1,1,n,w[x],w[x]+size[x]-1,k);
}
inline int Query_Son(int x)
{
    return Query(1,1,n,w[x],w[x]+size[x]-1);
}
int main()
{
    n=read(),m=read(),r=read(),p=read();
    for(int i=1;i<=n;i++)val[i]=read()%p;
    for(int i=1;i<n;i++)insert(read(),read());
    dep[r]=1;
    dfs1(r);
    dfs2(r,r);
    Build(1,1,n);
    while(m--)
    {
        int f=read();
        if(f==1){int x=read(),y=read(),z=read()%p;Modify_Tree(x,y,z);}
        if(f==2)printf("%d\n",Query_Tree(read(),read()));
        if(f==3){int x=read(),y=read()%p;Modify_Son(x,y);}
        if(f==4)printf("%d\n",Query_Son(read()));
    }
    return 0;
}

以上是关于树链剖分的主要内容,如果未能解决你的问题,请参考以下文章

树链剖分小结

树链剖分详解

树链剖分

树链剖分 入门

树链剖分

树链剖分(轻/重链剖分学习笔记)