• 【转载】树链剖分.By.Xminh


    轻重链剖分

    其实就是俗称的树链剖分。
    PS:树链剖分不止有轻重链剖分。但是大多数时候的树链剖分指的就是轻重链剖分。

    dfs序

    给树的节点重新编号,使得任意一个节点满足子树的dfs序都比它要大,而且它子树的dfs序是一段连续的区间。

    轻重链剖分的性质

    一种特殊的dfs序。
    满足每个节点的子树dfs序是一段连续的区间。
    满足树上的任意一条路径最多是由logn段连续的区间构成,就是说树链剖分剖出来的序就是满足这样一个性质,可以把对于路径的操作拆成对O(log n)段线性的序列的操作,进而可以用数据结构来维护。

    如何进行轻重链剖分?

    对树进行深度优先遍历,得到每个节点的重儿子,即它的儿子中子树节点最多的一个。再一次进行深度优先遍历,优先访问重儿子。这样遍历过的树能够满足以上性质。
    对于路径(x,y)的操作,我们一般要去找这两点所在的连续的一段的最顶端是哪个节点。我们把x设为段头深度较深的那一个(就是客观高度比较矮),然后跳上去找y的段头,这个段头用top数组在第二遍dfs时记录。top即当前点最高到哪个点能够成一条dfs序连续的路径。然后不断上跳到段头,直到跳到一条重链上(top值不一样即不在同一条重链上)。

    轻重链剖分能干什么?

    序列转化:把树上的路径转化为区间,如上。
    求LCA(思路跟求xy的重链相似)
    求LA(在logn的复杂度内查询x的深度为d的祖先是谁):倍增可做,但树链剖分也可以做。这里注意,不是向上走深度为d的祖先,而是客观深度为d的祖先是谁。先看当前点的top是不是比d还深,如果是的话跳到top的父亲,直到跳到x和这个深度为d的祖先在同一条重链上,即它们的dfs序是连续的,所以这时我们就可以列出公式deep[x]-deep[y]=dfn[x]-dfn[y]。所以在第二遍dfs我们要记录一下idfn,即dfn的映射,即idfn[dfn[y]]=y,我们最终得到的值就是idfn[d-deep[x]+dfn[x]]。

    luogu3384 树链剖分

    注释内容是我犯过的那些脑残错误……

    #include<iostream>
    #include<cstdio> 
    #include<cstring>
    #define rint register int 
    #define inv inline void
    #define ini inline int
    #define mid (l+r>>1)
    #define ls (num<<1)
    #define rs (num<<1|1)
    #define maxn 200020
    using namespace std;
    int n,m,rt,mod,cnt,dfn_clock;
    int a[maxn],tot[maxn],f[maxn],head[maxn],deep[maxn],sum[maxn<<2],add[maxn<<2],top[maxn],dfn[maxn],son[maxn],idfn[maxn];
    ini read()
    {
        char c;int r=0,f=1;
        while (c<'0' || c>'9')
        {
            if (c=='-') f=-1;
            c=getchar();
        }
        while (c>='0' && c<='9')
        {
            r=r*10+c-'0';
            c=getchar();
        }
        return r*f;
    }
    struct node
    {
        int next,to;
    }ljb[maxn];
    //因为要建双向边,所以maxn应该是n的两倍 
    inv add_edge(int x,int y)
    {
        ljb[++cnt].next=head[x];
        ljb[cnt].to=y;
        head[x]=cnt;
    }
    inv dfs1(int x,int dep,int fa)
    {
        deep[x]=dep;
        f[x]=fa;
        tot[x]=1;
        int maxx=-1;
        for (rint i=head[x];i;i=ljb[i].next)
        {
            int y=ljb[i].to;
            if (y!=fa)
            {
                dfs1(y,dep+1,x);
                tot[x]+=tot[y];
                if (tot[y]>maxx) 
                {
                    maxx=tot[y];
                    son[x]=y;
                }
            }
        }
    }
    inv dfs2(int x,int tp)
    {
        dfn[x]=++dfn_clock;
        idfn[dfn_clock]=x;
        top[x]=tp;
        if (!son[x]) return;
        dfs2(son[x],tp);
        for (rint i=head[x];i;i=ljb[i].next)
        {
            int y=ljb[i].to;
            if (y!=f[x] && y!=son[x])
                dfs2(y,y);
        }
    }
    inv pushdown(int num,int ln,int rn)
    {
        if (add[num])
        {
            add[ls]+=add[num];
            add[rs]+=add[num];
            sum[ls]=(sum[ls]+ln*add[num])%mod;
            sum[rs]=(sum[rs]+rn*add[num])%mod;
            //此处不是仅仅加上add[num]就可以的 
            //别忘了%mod 
            add[num]=0;
        }
    }
    inv build(int l,int r,int num)
    {
        if (l==r)
        {
            sum[num]=a[idfn[l]]%mod;
            //idfn保存dfs序对应的本来的数字 
            //这里建树是针对第二遍的dfs序
            //因为只有第二遍dfs序才能把路径转成区间 
            return;
        }
        build(l,mid,ls);
        build(mid+1,r,rs);
        sum[num]=(sum[ls]+sum[rs])%mod;
    }
    inv change(int L,int R,int l,int r,int x,int num)
    {
        if (L<=l && r<=R) 
        {
            sum[num]+=(r-l+1)*x;
            add[num]+=x;
            return;
        }
        pushdown(num,mid-l+1,r-mid);
        //zz错误→→→↑↑ 
        if (L<=mid) change(L,R,l,mid,x,ls);
        if (R>mid) change(L,R,mid+1,r,x,rs);
        //大写L和R 
        sum[num]=(sum[ls]+sum[rs])%mod;
    }
    ini query(int L,int R,int l,int r,int num)
    {
        if (L<=l && r<=R) return sum[num]%mod;
        pushdown(num,mid-l+1,r-mid);
        int ans=0;
        if (L<=mid) ans=(ans+query(L,R,l,mid,ls))%mod;
        if (R>mid) ans=(ans+query(L,R,mid+1,r,rs))%mod;
        return ans;
    }
    inv treeadd(int x,int y,int C)
    {
        C%=mod;
        int t1=top[x];
        int t2=top[y];
        int ans=0;
        while (t1!=t2)
        {
            if (deep[t1]<deep[t2])
            {
                swap(x,y);
                swap(t1,t2);
            }
            //这里和lca不一样,lca是低的往高的上面跳,直到跳到同一深度
            //这个是不断交替上跳
            change(dfn[t1],dfn[x],1,n,C,1);
            x=f[t1];
            t1=top[x];
        }
        if (deep[x]>deep[y]) swap(x,y);
        change(dfn[x],dfn[y],1,n,C,1);
    }
    ini treeques(int x,int y)
    {
        int t1=top[x];
        int t2=top[y];
        int ans=0;
        while (t1!=t2)
        {
            if (deep[t1]<deep[t2])
            {
                swap(x,y);
                swap(t1,t2);
            }
            ans=(ans+query(dfn[t1],dfn[x],1,n,1))%mod;
            x=f[t1];
            t1=top[x];
        }
        if (deep[x]>deep[y]) swap(x,y);
        ans=(ans+query(dfn[x],dfn[y],1,n,1))%mod;
        return ans;
    }
    int main()
    {
        n=read();m=read();rt=read();mod=read();
        for (rint i=1;i<=n;i++) a[i]=read();
        for (rint i=1;i<=n-1;i++)
        {
            int x,y;
            x=read();y=read();
            add_edge(x,y);
            add_edge(y,x);
        }
        dfs1(rt,1,rt);
        dfs2(rt,rt);
        build(1,n,1);
        for (rint i=1;i<=m;i++)
        {
            int opt,x,y,z;
            opt=read();
            if (opt==1) 
            {
                x=read();y=read();z=read();
                treeadd(x,y,z);
            }
            if (opt==2)
            {
                x=read();y=read();
                printf("%d
    ",treeques(x,y));
            }
            if (opt==3)
            {
                x=read();z=read();
                change(dfn[x],dfn[x]+tot[x]-1,1,n,z,1);
            }
            if (opt==4)
            {
                x=read();
                printf("%d
    ",query(dfn[x],dfn[x]+tot[x]-1,1,n,1));
                //这里是dfn[x]而不是x 
            }
        }
    }
    
  • 相关阅读:
    决策表快速排序
    书摘
    读书笔记
    echarts x y轴设置
    echarts图类型设置
    echarts入门
    jqgride实现多选
    jqgride实现每一行的单选
    Mac react环境搭建
    两列布局,三列布局
  • 原文地址:https://www.cnblogs.com/victorique/p/8488330.html
Copyright © 2020-2023  润新知