首页 > 其他 > 详细

【模板】树链剖分

时间:2019-10-05 17:27:19      阅读:56      评论:0      收藏:0      [点我收藏+]

前言:蒟蒻刚刚学会树链剖分,写一篇博客以加深理解

 洛谷 P3384 【模板】树链剖分

树链剖分:指一种对树进行划分的算法,它先通过轻重边剖分将树分为多条链,保证每个点属于且只属于一条链,然后再通过数据结构(树状数组、BST、SPLAY、线段树等)来维护每一条链。

                                ————百度百科

树链剖分中有如下定义:

重儿子:父亲节点的儿子中以此点为根节点的子树节点数量最多的子节点

轻儿子:父亲节点的儿子中除重儿子外的节点

重边:父亲节点和重儿子连接而成的边

轻边:父亲节点和轻儿子连接而成的边

重链:重边连接而成的链

轻链:轻边连接而成的链

链头:一条链上深度最小的节点

 

了解了树剖的定义,回头再看题。

题目要求在树上实现四种操作:

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

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

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

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

 

对于一棵树,我们先进行树链剖分——

1:Dfs1()维护节点深度,父节点编号,子树节点数,如下

void Dfs1(int f,int fa,int deep){//f为当前节点,fa为当前节点的父节点,deep为当前节点的深度
    depth[f]=deep;
    Fa[f]=fa;
    sonnum[f]=1;//初始化孩子数(包括自己)
    int heavyson=-1;
    for(int i=head[f];i;i=edge[i].next){
        int u=edge[i].to;
        if(u==fa) continue;
        Dfs1(edge[i].to,f,deep+1);
        sonnum[f]+=sonnum[u];//向上传递孩子数
        if(sonnum[u]>heavyson) heavyson=sonnum[u],HeavySon[f]=u;
    }
}

2.Dfs2()维护剖过的数节点新编号、初始值与节点所在链的顶部节点编号,如下

void Dfs2(int f,int Top){//f为当前节点,Top为当前节点所在链的顶端
    newid[f]=++tot;//新编号
    neww[tot]=w[f];//新编号赋初值
    top[f]=Top;//维护链顶编号
    if(!HeavySon[f]) return;//如果Dfs到叶子节点就return
    Dfs2(HeavySon[f],Top);
    for(int i=head[f];i;i=edge[i].next){
        int u=edge[i].to;
        if(u==Fa[f]||u==HeavySon[f]) continue;
        Dfs2(u,u);
    }
}

 

手画一棵树并推一下会发现同一条链上的节点新编号都是连续的,这样便于我们用线段树进行维护

线段树维护方式与线段树模板无异

 

最后贴出AC代码:

#include<bits/stdc++.h>
using namespace std;
struct Edge{
    int to;
    int next;
}edge[200005];
int n,m,r,p;
int cnt=0,tot=0,flag,x,y,z;
long long head[200005];
long long w[100005],depth[100005],Fa[100005],sonnum[100005];
long long HeavySon[100005],top[100005],ans[400005],tag[400005];
long long newid[100005],neww[100005];
void add(int x,int y){
    edge[++cnt].to=y;
    edge[cnt].next=head[x];
    head[x]=cnt;
}
void Dfs1(int f,int fa,int deep){
    depth[f]=deep;
    Fa[f]=fa;
    sonnum[f]=1;
    int heavyson=-1;
    for(int i=head[f];i;i=edge[i].next){
        int u=edge[i].to;
        if(u==fa) continue;
        Dfs1(edge[i].to,f,deep+1);
        sonnum[f]+=sonnum[u];
        if(sonnum[u]>heavyson) heavyson=sonnum[u],HeavySon[f]=u;
    }
}
void Dfs2(int f,int Top){
    newid[f]=++tot;
    neww[tot]=w[f];
    top[f]=Top;
    if(!HeavySon[f]) return;
    Dfs2(HeavySon[f],Top);
    for(int i=head[f];i;i=edge[i].next){
        int u=edge[i].to;
        if(u==Fa[f]||u==HeavySon[f]) continue;
        Dfs2(u,u);
    }
}
void pushup(int k){
    ans[k]=ans[k<<1]+ans[k<<1|1];
    ans[k]%=p;
}
void build(int k,int l,int r){
    tag[k]=0;
    if(l==r){
        ans[k]=neww[l];
        ans[k]%=p;
        return;
    }
    int mid=(l+r)>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
    pushup(k);
}
void ff(int k,int l,int r,int delta){
    tag[k]=tag[k]+delta;
    ans[k]=ans[k]+delta*(r-l+1);
}
void pushdown(int k,int l,int r){
    int mid=(l+r)>>1;
    ff(k<<1,l,mid,tag[k]);
    ff(k<<1|1,mid+1,r,tag[k]);
    tag[k]=0;
}
void update(int k,int l,int r,int l1,int r1,int delta){
    if(l1<=l&&r<=r1){
        ans[k]+=delta*(r-l+1);
        tag[k]+=delta;
        return;
    }
    pushdown(k,l,r);
    int mid=(l+r)>>1;
    if(l1<=mid) update(k<<1,l,mid,l1,r1,delta);
    if(r1>mid) update(k<<1|1,mid+1,r,l1,r1,delta);
    pushup(k);
}
int query(int k,int l,int r,int l1,int r1){
    int step=0;
    if(l1<=l&&r<=r1) return ans[k]%p;
    pushdown(k,l,r);
    int mid=(l+r)>>1;
    if(l1<=mid) step+=query(k<<1,l,mid,l1,r1),step%=p; 
    if(r1>mid) step+=query(k<<1|1,mid+1,r,l1,r1),step%=p;
    return step%p;
}
void Tag1(int x,int y,int delta){
    delta%=p;
    while(top[x]!=top[y]){
        if(depth[top[x]]<depth[top[y]]) swap(x,y);
        update(1,1,n,newid[top[x]],newid[x],delta);
        x=Fa[top[x]];
    }
    if(depth[x]>depth[y]) swap(x,y);
    update(1,1,n,newid[x],newid[y],delta);
}
int Tag2(int x,int y){
    int step=0;
    while(top[x]!=top[y]){
        if(depth[top[x]]<depth[top[y]]) swap(x,y);
        step+=query(1,1,n,newid[top[x]],newid[x]);
        step%=p;
        x=Fa[top[x]];
    }
    step+=query(1,1,n,min(newid[x],newid[y]),max(newid[x],newid[y]));
    step%=p;
    return step;
}
void Tag3(int x,int delta){
    delta%=p;
    update(1,1,n,newid[x],newid[x]+sonnum[x]-1,delta);
}
int Tag4(int x){
    return query(1,1,n,newid[x],newid[x]+sonnum[x]-1);
}
int main(){
    scanf("%d%d%d%d",&n,&m,&r,&p);
    for(int i=1;i<=n;i++) scanf("%lld",&w[i]);
    for(int i=1;i<=n-1;i++){
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);        
    }
    Dfs1(r,0,1);
    Dfs2(r,r);
    build(1,1,n);
    for(int i=1;i<=m;i++){
        scanf("%d",&flag);
        if(flag==1){
            scanf("%d%d%d",&x,&y,&z);
            Tag1(x,y,z);
        }
        else if(flag==2){
            scanf("%d%d",&x,&y);
            printf("%lld\n",Tag2(x,y)%p);
        }
        else if(flag==3){
            scanf("%d%d",&x,&z);
            Tag3(x,z);
        }
        else{
            scanf("%d",&x);
            printf("%lld\n",Tag4(x)%p);
        }
    }
    return 0;
}

 

完结撒花!

【模板】树链剖分

原文:https://www.cnblogs.com/WwaiForever/p/11625000.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!