树链剖分详解 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;
}
原文地址:https://www.cnblogs.com/charlesss/p/10474190.html