树链剖分学习笔记

Q:什么是树链剖分?
A:在一棵树上,划分轻重链来维护树上的区间(语言表达能力极差所以我决定复制一段过来

在一棵树上进行路径的修改、求极值、求和”乍一看只要线段树就能轻松解决,实际上,仅凭线段树是不能搞定它的。我们需要用到一种貌似高级的复杂算法——树链剖分。树链,就是树上的路径。剖分,就是把路径分类为重链和轻链。

树链剖分有几个操作,dfs1预处理出每个节点的size(质量,以i为根的节点有多少孩子节点),hson(最重的一个孩子节点),fa父亲节点和deep深度。dfs2划分轻重链,先遍历重儿子,记录top(链头元素),把树中节点映射到线段树里(赋一个递增值到id数组然后记录下这个值真实对应的点)。然后再遍历其他轻儿子(轻儿子的链头元素为他自己)。
(讲的还是好乱啊。。

然后用数据结构来维护(下面代码是线段树。)

然后怎么对树上元素操作呢?我们发现,在同一个链上的元素的虚拟的序号(就是你之前映射到线段树里的那个)是连续的。所以同一个链上的两点查询修改就是非常容易,直接对那个id区间修改就好了。记得dep小的是区间左端点。

那么怎么对不同链上的元素操作呢。我们可以让这两个链头往上跳(跳到上一个链的链头。)边跳边修改,直到跳到一个同一个链上。

——————以上的乱乱的思路。
以下是丑丑的代码:

#include<bits/stdc++.h>
using namespace std;

const int MAXN=1e5+5;

int head[MAXN],cnt=0,n,m,r,p;
struct edge{
    int to,next;
}e[MAXN<<1];
inline void add(int u,int v){e[++cnt]=(edge){v,head[u]},head[u]=cnt;}

int fa[MAXN],hson[MAXN],size[MAXN],dep[MAXN];

void dfs1(int u,int father){
    fa[u]=father;
    size[u]=1;
    dep[u]=dep[father]+1;
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        if(v==father)continue;
        dfs1(v,u);
        size[u]+=size[v];
        if(size[v]>size[hson[u]]||!hson[u])hson[u]=v;
    }   
}

int top[MAXN],id[MAXN],real1[MAXN],num=0;

void dfs2(int u,int tp){
    top[u]=tp;
    id[u]=++num;
    real1[num]=u;
    if(hson[u])dfs2(hson[u],tp);
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        if(v==fa[u]||v==hson[u])continue;
        dfs2(v,v);
    }
}


#define mid ((l+r)>>1)
#define lson (o<<1)
#define rson (o<<1|1)

int sumv[MAXN<<2],addv[MAXN<<2],a[MAXN];

inline void pushup(int o){sumv[o]=(sumv[lson]+sumv[rson])%p;}

inline void pushdown(int o,int l,int r){
    if(addv[o]){
        (addv[lson]+=addv[o])%=p;(addv[rson]+=addv[o])%=p;
        (sumv[lson]+=addv[o]*(mid-l+1))%=p;(sumv[rson]+=addv[o]*(r-mid))%=p;
        addv[o]=0;
    }
}

inline void bulidt(int o,int l,int r){
    addv[o]=0;
    if(l==r){sumv[o]=a[real1[l]];return;}
    bulidt(lson,l,mid);bulidt(rson,mid+1,r);
    pushup(o);
} 

inline long long query(int o,int l,int r,int ql,int qr){
    if(ql<=l&&qr>=r)return sumv[o];
    long long ans=0;
    pushdown(o,l,r);
    if(ql<=mid)(ans+=query(lson,l,mid,ql,qr))%=p;
    if(qr>mid)(ans+=query(rson,mid+1,r,ql,qr))%=p;
    return ans;
}

inline void change(int o,int l,int r,int ql,int qr,long long ww){
    if(ql<=l&&qr>=r){(sumv[o]+=(r-l+1)*ww)%=p;(addv[o]+=ww)%=p;return;}
    pushdown(o,l,r);
    if(ql<=mid)change(lson,l,mid,ql,qr,ww);
    if(qr>mid)change(rson,mid+1,r,ql,qr,ww);
    pushup(o);
}


inline void chain_add(int u,int v,long long ww){
    int fx=top[u],fy=top[v];
    while(fx!=fy){
        if(dep[fx]>dep[fy]){
            change(1,1,n,id[fx],id[u],ww);
            u=fa[fx];
        }
        else {
            change(1,1,n,id[fy],id[v],ww);
            v=fa[fy];
        }
        fx=top[u],fy=top[v];
    }
    if(dep[u]<dep[v])swap(u,v);
    change(1,1,n,id[v],id[u],ww);
}

inline long long chain_chazhao(int u,int v){
    int fx=top[u],fy=top[v];
    long long ans=0;
    while(fx!=fy){
        if(dep[fx]>dep[fy]){
            (ans+=query(1,1,n,id[fx],id[u]))%=p;
            u=fa[fx];
        }
        else {
            (ans+=query(1,1,n,id[fy],id[v]))%=p;
            v=fa[fy];
        }
        fx=top[u],fy=top[v];
    }
    if(dep[u]<dep[v])swap(u,v);
    (ans+=query(1,1,n,id[v],id[u]))%=p;
    return ans%p;
}

inline long long zishu(int u){return query(1,1,n,id[u],id[u]+size[u]-1);}

inline long long gaizishu(int u,long long ww){
    change(1,1,n,id[u],id[u]+size[u]-1,ww);
}

int main(){
    scanf("%d%d%d%d",&n,&m,&r,&p);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    for(int i=1;i<n;i++){
        int tem1,tem2;
        scanf("%d%d",&tem1,&tem2);
        add(tem1,tem2);
        add(tem2,tem1);
    }
    dfs1(r,r);
    dfs2(r,r);
    bulidt(1,1,n);
    for(int i=1;i<=m;i++){
        int leixing,zuo,you,val;
        scanf("%d",&leixing);
        if(leixing==1){
            scanf("%d%d%d",&zuo,&you,&val);
            chain_add(zuo,you,val);
        }
        if(leixing==2){
            scanf("%d%d",&zuo,&you);
            printf("%lld\n",chain_chazhao(zuo,you));
        }
        if(leixing==3){
            scanf("%d%d",&zuo,&val);
            gaizishu(zuo,val);
        }
        if(leixing==4){
            scanf("%d",&zuo);
            printf("%lld\n",zishu(zuo));
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值