zoukankan      html  css  js  c++  java
  • 树链剖分详解 luoguP3384【模板】树链剖分

    一:用处

      对一棵树分成几条链,把树形变为线性,减少处理难度
      需要处理的问题:

        1.将树从x到y结点最短路径上所有节点的值都加上z

        2.求树从x到y结点最短路径上所有节点的值之和

        3.将以x为根节点的子树内所有节点值都加上z

        4.求以x为根节点的子树内所有节点值之和

    二:相关概念:

    1.重儿子:对于每一个非叶子节点,它的儿子中以那个儿子为根的子树节点数(包括自身)最大的儿子为该节点的重儿子

    2.轻儿子:非重儿子

    3.重边:一个父节点与其重儿子的连边

    4.轻边:非重边

    5.重链:相邻重边相连形成的链

    tips:叶子结点若为轻儿子,则自成一条长度为1的链

      重链以轻儿子或根节点为起点

    三:常规操作

    1.预处理

    (1)dfs1:

    处理:

      各点深度;  各点重儿子编号; 各点最大子树大小 ;各点父亲

    代码:

    void dfs1(int now,int fa,int deep)
    {
        siz[now]=1;
        fat[now]=fa;
        dep[now]=deep;
        zs=-1;
        for(int i=head[now];i;i=nxt[i])
        {
            if(to[i]==now)
                continue; //别忘了 
            dfs1(to[i],now,deep+1);
            siz[now]+=siz[to[i]];
            if(siz[to[i]]>zs)//处理重儿子 
                zhoson[now]=to[i],zs=siz[to[i]];
        }
    }

    (2)dfs2

    处理:

       标记每个点的新编号(线段树中编号)

       赋值每个点的初始值到新编号上

       处理每个点所在链的顶端

       处理每条链

    顺序规定:先重后轻

    代码:

    void dfs2(int now,int top)
    {
        tp[now]=top;
        id[now]=++cnt;
        wt[cnt]=w[x];
        if(!zhoson[now])
            return;
        dfs2(zhoson[now],top);
        for(int i=head[now];i;i=nxt[i])
        {
            if(to[i]==fat[now]||to[i]==zhoson[now])
                continue;
            dfs2(to[i],to[i]);
        }
    } 

     2.功能实现

      1.查询两点间距离

      方法:每次选定两点所在链中深度较大的,加上它到其链起点的权值,然后跳到链起点的父节点重复操作,直至两点在同一条链上。

      

    int qiurange(int x,int y)
    {
        int ans=0;
        while(top[x]!=top[y])
        {
            if(deep[top[x]]<deep[top[y]])
                swap(x,y);
            res=0;
            query(1,1,n,tid[top[x]],tid[x]);
            ans=(ans+res)%mod;
            x=fat[top[x]];
        }
    
        if(deep[x]>deep[y])
        swap(x,y);
        res=0;
        query(1,1,n,tid[x],tid[y]);
        ans=(ans+res)%mod;
        return ans;
    }

      2.两点间路径修改:

      同1,反复跳并update(线段树)

    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,1,n,tid[top[x]],tid[x],k);
            x=fat[top[x]];
        }
        if(deep[x]>deep[y])
        swap(x,y);
        update(1,1,n,tid[x],tid[y],k);
    }

    3.子树修改

    上边提到过,子树中节点的编号是连续的,因此在线段树上也是一段连续的区间,相当于线段树区间修改

    void updSon(int x,int k)
    {
        update(1,1,n,tid[x],tid[x]+siz[x]-1,k);
    }

    4.子树查询

    只要直接维护即可。(区间的起点是该子树根节点,大小是子树大小)

    int qSon(int x)
    {
        res=0;
        query(1,1,n,tid[x],tid[x]+siz[x]-1);
        return res;
    }

    完整代码如下,除了上面的部分还要有线段树的基本操作函数(因此码量感人)


    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #define maxn 100005
    #define mid (l+r>>1)
    #define len (r-l+1)
    #define ls id<<1
    #define rs id<<1|1
    using namespace std;
    int cnt,head[maxn],nxt[maxn << 1],to[maxn << 1];
    int w[maxn],wt[maxn],tidnum,siz[maxn],deep[maxn],son[maxn],fat[maxn],tid[maxn],top[maxn];
    int a[maxn << 2],laz[maxn << 2];
    int res=0,n,m,r,mod;
    void addedge(int x,int y)
    {
        to[++cnt]=y;
        nxt[cnt]=head[x];
        head[x]=cnt;
    }
    void pushdown(int rt,int l,int r)
    {
        (laz[rt<<1]+=laz[rt]) %= mod;
        (laz[rt<<1|1]+=laz[rt]) %= mod;
        a[rt<<1]+=laz[rt]*(mid - l + 1);
        a[rt<<1|1]+=laz[rt]*(r - mid);
        a[rt<<1]%=mod;
        a[rt<<1|1]%=mod;
        laz[rt]=0;
    }
    
    void build(int id,int l,int r)
    {
        if(l==r)
        {
            a[id]=wt[l];
            if(a[id]>mod)
            a[id]%=mod;
            return;
        }
        build(ls,l,mid);
        build(rs,mid+1,r);
        a[id]=(a[id<<1]+a[id<<1|1])%mod;
    }
    
    void query(int id,int l,int r,int tol,int tor)
    {
        if(tol<=l&&r<=tor)
        {
            res+=a[id];
            res%=mod;
            return;
        }
        else
        {
            if(laz[id])
            pushdown(id,l,r);
            if(tol<=mid)
            query(ls,l,mid,tol,tor);
            if(tor>mid)
            query(rs,mid+1,r,tol,tor);
        }
    }
    
    void update(int id,int l,int r,int tol,int tor,int k)
    {
        if(tol<=l&&r<=tor)
        {
            laz[id]+=k;
            a[id]+=k*len;
            a[id] %= mod;
            laz[id] %= mod;
        }
        else
        {
            if(laz[id])
            pushdown(id,l,r);
            if(tol<=mid)
            update(ls,l,mid,tol,tor,k);
            if(tor>mid)
            update(rs,mid+1,r,tol,tor,k);
            a[id]=(a[id<<1]+a[id<<1|1])%mod;
        }
    }
    
    int qiurange(int x,int y)
    {
        int ans=0;
        while(top[x]!=top[y])
        {
            if(deep[top[x]]<deep[top[y]])
                swap(x,y);
            res=0;
            query(1,1,n,tid[top[x]],tid[x]);
            ans=(ans+res)%mod;
            x=fat[top[x]];
        }
    
        if(deep[x]>deep[y])
            swap(x,y);
        res=0;
        query(1,1,n,tid[x],tid[y]);
        ans=(ans+res)%mod;
        return ans;
    }
    
    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,1,n,tid[top[x]],tid[x],k);
            x=fat[top[x]];
        }
        if(deep[x]>deep[y])
        swap(x,y);
        update(1,1,n,tid[x],tid[y],k);
    }
    
    int qSon(int x)
    {
        res=0;
        query(1,1,n,tid[x],tid[x]+siz[x]-1);
        return res;
    }
    
    void updSon(int x,int k)
    {
        update(1,1,n,tid[x],tid[x]+siz[x]-1,k);
    }
    
    void dfs1(int x,int f,int deeep) 
    {
        deep[x]=deeep;
        fat[x]=f;
        siz[x]=1;
        int maxson=-1;
        for(int i=head[x];i;i=nxt[i])
        {
            int y=to[i];
            if(y==f)
            continue;
            dfs1(y,x,deeep+1); 
            siz[x]+=siz[y]; 
            if(siz[y]>maxson)
            son[x]=y,maxson=siz[y];
        }
    }
    
    void dfs2(int id,int tp)
    {
        tid[id]=++tidnum;
        wt[tidnum]=w[id];
        top[id]=tp;
        if(!son[id])
        return; 
        dfs2(son[id],tp);
        for(int i=head[id];i;i=nxt[i])
        {
            if(to[i]==fat[id]||to[i]==son[id])
            continue;
            dfs2(to[i],to[i]);
        }
    }
    
    int main()
    {
        scanf("%d%d%d%d",&n,&m,&r,&mod);
        for(int i=1;i<=n;i++)
        scanf("%d",&w[i]);
        for(int i=1;i<n;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            addedge(a,b);
            addedge(b,a);
        }
        dfs1(r,0,1);
        dfs2(r,r);
        build(1,1,n);
        while(m--)
        {
            int t,x,y,z;
            scanf("%d",&t);
            if(t==1)
            {
                scanf("%d%d%d",&x,&y,&z);
                uprange(x,y,z);
            }
            else if(t==2)
            {
                scanf("%d%d",&x,&y);
                printf("%d
    ",qiurange(x,y));
            }
            else if(t==3)
            {
                scanf("%d%d",&x,&y);
                updSon(x,y);
            }
            else
            {
                scanf("%d",&x);
                printf("%d
    ",qSon(x));
            }
        }
        return 0;
    }
  • 相关阅读:
    Study Plan The TwentySecond Day
    Study Plan The Nineteenth Day
    Study Plan The TwentySeventh Day
    Study Plan The Twentieth Day
    Study Plan The TwentyFirst Day
    python实现进程的三种方式及其区别
    yum makecache
    JSONPath 表达式的使用
    oracle执行cmd的实现方法
    php daodb插入、更新与删除数据
  • 原文地址:https://www.cnblogs.com/charlesss/p/10474190.html
Copyright © 2011-2022 走看看