树链剖分

栏目: 数据库 · 发布时间: 5年前

内容简介:题目链接:感觉洛谷的树剖模板出的挺好的,涉及的面比一些省选题要多得多。众所周知,对树上两个不相邻节点的操作,若转化为链上的操作,则会方便许多。

题目链接: https://www.luogu.org/problemnew/show/P3384

感觉洛谷的树剖模板出的挺好的,涉及的面比一些省选题要多得多。

树链剖分

众所周知,对树上两个不相邻节点的操作,若转化为链上的操作,则会方便许多。

顾名思义,树链剖分就是把 一棵树分成几条链,把树形变为线性,减少处理难度。

需要提前掌握的知识:, 树形, 树的序, 线段树。

需要知道的概念:

  • 重儿子:对于每一个非叶子节点,它的儿子中 儿子数量最多的那一个儿子 为该节点的重儿子
  • 轻儿子:对于每一个非叶子节点,它的儿子中 非重儿子 的剩下所有儿子即为轻儿子
  • 叶子节点没有重儿子也没有轻儿子(因为它没有儿子。。)
  • 重边:连接任意两个重儿子的边叫做重边
  • 轻边:剩下的即为轻边
  • 重链:相邻重边连起来的 连接一条重儿子 的链叫重链
  • 对于叶子节点,若其为轻儿子,则有一条以自己为起点的长度为1的链
  • 每一条重链以轻儿子为起点

树剖分为一些部分:

  • 1.深搜处理出每个点的深度,父亲节点,包括它自己的子树节点大小,和重儿子的编号。
  • 2.深搜处理出每个点在新链上的编号,以及链顶的编号。
  • 3.建一颗线段树,来进行区间的操作。

dfs1

void dfs1(int x,int last,int depth)
{
    father[x]=last;
    dep[x]=depth;
    sonnum[x]=1;
    int maxnum=-1;
    for(int i=head[x];i;i=edge[i].nextt)
    {
        int to=edge[i].to;
        if(to==last) continue;
        dfs1(to,x,depth+1);
        sonnum[x]+=sonnum[to];
        if(sonnum[to]>maxnum) maxnum=sonnum[to],son[x]=to;
    }
}

应该比较好理解。。。

dfs2

void dfs2(int x,int topx)
{
    ncnt[x]=++cnt;
    nval[cnt]=val[x]%mod;
    top[x]=topx;
    if(!son[x]) return ;
    dfs2(son[x],topx);
    for(int i=head[x];i;i=edge[i].nextt)
    {
        int to=edge[i].to;
        if(to==father[x]||to==son[x]) continue;
        dfs2(to,to);
    }
}

要先处理重儿子,因为要保证是一条编号连续的重链。

要处理的一些问题

  • 处理任意两点间路径上的点权和
  • 处理一点及其子树的点权和
  • 修改任意两点间路径上的点权
  • 修改一点及其子树的点权

处理任意两点间路径上的点权和

inline int qRange(int x,int y)
{
    int ans=0;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        ans=(ans%mod+Tree.query(1,1,n,ncnt[top[x]],ncnt[x])%mod)%mod;
        x=father[top[x]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    ans=(ans%mod+Tree.query(1,1,n,ncnt[x],ncnt[y])%mod)%mod;
    return ans%mod;
}

就是按深度,像一样,选取一个低的点往上跳,对树上的区间依次操作。

处理一点及其子树的点权和

printf("%d\n",Tree.query(1,1,n,ncnt[x],ncnt[x]+sonnum[x]-1)%mod);

先前统计出了子树大小,因为编号连续,所以直接查询连续区间即可。

修改任意两点间路径上的点权

inline void updRange(int x,int y,int z)
{
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        Tree.update(1,1,n,ncnt[top[x]],ncnt[x],z);
        x=father[top[x]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    Tree.update(1,1,n,ncnt[x],ncnt[y],z);
}

原理与上面查询类似。

修改一点及其子树的点权

Tree.update(1,1,n,ncnt[x],ncnt[x]+sonnum[x]-1,z%mod);

就。。没什么了

全部代码:

#include <cstdio>
#include <cctype>
#include <cstring> 
#include <iostream>
#define ls p<<1
#define rs p<<1|1
static const int MAXN=200050; 
using namespace std;
struct Edge
{
    int to,nextt;
}edge[MAXN];
int n,m,r,mod,ins,u,v,x,y,z,cnt,num,head[MAXN],val[MAXN],top[MAXN],ncnt[MAXN],nval[MAXN],dep[MAXN],sonnum[MAXN],son[MAXN],father[MAXN];
inline int read()
{
    int x=0;bool sign=false;
    char alpha=0;
    while(!isdigit(alpha)) sign|=alpha=='-',alpha=getchar();
    while(isdigit(alpha)) x=(x<<1)+(x<<3)+(alpha^48),alpha=getchar();
    return sign?-x:x;
}
int a[MAXN<<2],tag[MAXN<<2];
struct Segment_Tree
{
    inline void push_up(int p){a[p]=(a[ls]+a[rs])%mod;}
    inline void push_down(int p,int l,int r,int mid)
    {
        tag[ls]+=tag[p];
        tag[rs]+=tag[p];
        a[ls]+=((mid-l+1)*tag[p])%mod;
        a[rs]+=((r-mid)*tag[p])%mod;
        tag[p]=0;
    }
    void build(int p,int l,int r)
    {
        if(l==r)
        {
            a[p]=nval[l]%mod;
            return ;
        }
        int mid=(l+r)>>1;
        build(ls,l,mid);
        build(rs,mid+1,r);
        push_up(p);
    }
    void update(int p,int l,int r,int ql,int qr,int z)
    {
        if(ql<=l&&qr>=r)
        {
            a[p]+=((r-l+1)*z)%mod;
            tag[p]+=z;
            return ;
        }
        int mid=(l+r)>>1;
        if(tag[p]) push_down(p,l,r,mid);
        if(ql<=mid) update(ls,l,mid,ql,qr,z);
        if(qr>mid) update(rs,mid+1,r,ql,qr,z);
        push_up(p);
    }
    int query(int p,int l,int r,int ql,int qr)
    {
        if(ql<=l&&qr>=r) return a[p]%mod;
        int mid=(l+r)>>1,sum=0;
        if(tag[p]) push_down(p,l,r,mid);
        if(ql<=mid) sum+=query(ls,l,mid,ql,qr);
        if(qr>mid) sum+=query(rs,mid+1,r,ql,qr);
        push_up(p);
        return sum%mod;
    }
}Tree;
inline void addedge(int u,int v)
{
    edge[++num].to=v;
    edge[num].nextt=head[u];
    head[u]=num;
}
void dfs1(int x,int last,int depth)
{
    father[x]=last;
    dep[x]=depth;
    sonnum[x]=1;
    int maxnum=-1;
    for(int i=head[x];i;i=edge[i].nextt)
    {
        int to=edge[i].to;
        if(to==last) continue;
        dfs1(to,x,depth+1);
        sonnum[x]+=sonnum[to];
        if(sonnum[to]>maxnum) maxnum=sonnum[to],son[x]=to;
    }
}
void dfs2(int x,int topx)
{
    ncnt[x]=++cnt;
    nval[cnt]=val[x]%mod;
    top[x]=topx;
    if(!son[x]) return ;
    dfs2(son[x],topx);
    for(int i=head[x];i;i=edge[i].nextt)
    {
        int to=edge[i].to;
        if(to==father[x]||to==son[x]) continue;
        dfs2(to,to);
    }
}
inline void updRange(int x,int y,int z)
{
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        Tree.update(1,1,n,ncnt[top[x]],ncnt[x],z);
        x=father[top[x]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    Tree.update(1,1,n,ncnt[x],ncnt[y],z);
}
inline int qRange(int x,int y)
{
    int ans=0;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        ans=(ans%mod+Tree.query(1,1,n,ncnt[top[x]],ncnt[x])%mod)%mod;
        x=father[top[x]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    ans=(ans%mod+Tree.query(1,1,n,ncnt[x],ncnt[y])%mod)%mod;
    return ans%mod;
}
int main()
{
    n=read();m=read();
    r=read();mod=read();
    for(int i=1;i<=n;i++) val[i]=read();
    for(int i=1;i<=n-1;i++)
    {
        u=read();v=read();
        addedge(u,v);
        addedge(v,u);
    }
    dfs1(r,0,1);
    dfs2(r,r); 
    Tree.build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        ins=read();
        if(ins==1)
        {
            x=read();y=read();z=read();
            updRange(x,y,z%mod);
        } 
        else if(ins==2)
        {
            x=read();y=read();
            printf("%d\n",qRange(x,y)%mod);
        }
        else if(ins==3)
        {
            x=read();z=read();
            Tree.update(1,1,n,ncnt[x],ncnt[x]+sonnum[x]-1,z%mod);
        }
        else
        {
            x=read();
            printf("%d\n",Tree.query(1,1,n,ncnt[x],ncnt[x]+sonnum[x]-1)%mod);
        }
    }
    return 0;
}

以上所述就是小编给大家介绍的《树链剖分》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Probability and Computing: Randomization and Probabilistic Techn

Probability and Computing: Randomization and Probabilistic Techn

Michael Mitzenmacher、Eli Upfal / Cambridge University Press / 2017-7-3 / USD 62.23

Greatly expanded, this new edition requires only an elementary background in discrete mathematics and offers a comprehensive introduction to the role of randomization and probabilistic techniques in m......一起来看看 《Probability and Computing: Randomization and Probabilistic Techn》 这本书的介绍吧!

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具