树链剖分 复习笔记

前言

以前学树链剖分感觉好难,现在好多了,想写个笔记总结一下心得

引入

题目

洛谷P3384
在这里插入图片描述

思考

诶?怎么那么像线段树的题目。但是这可是在一棵树上,不是在一个数列上哦。或许可以把一棵树拆成好几个数列?

这样行么?

行,怎么不行。我们只要从树上挑去一个链,把这个链做成数列,不停循环下去直到这个树被分割完,不亦说乎?

其实我们所说的树就可以看成一个所有数列都仅为一个点的树

算法

效率最大化

那么,如何使算法效率最高?
我们想一想,如果每个数列都很短,那么,更改一个很长的链时就需要访问许多数列,耗费时间。所以要找到尽量长的链

重边

这里念“重量”的重
其他文章在这里讲得太严谨了,更难懂。在这里给一个通俗但是没那么严谨的解释

对于一个点,链接它的最重子树的边便是重边

要注意一点

如果有多个子树重量相同,则都是重边

反之,是轻边。

重链

就是连续重边连成的链。
反之叫做轻链。

建线段树

现在,把每个重链看成一条数列。
然后建线段树(或树状数组,这个看题目)

实现

古老代码,码风奇特。

#include<cstdio>
#include<algorithm>

using namespace std;

int head[100010], sum[400010], son[100010], laz[400010], w[100010], nw[100010], id[100010],  cnt, fa[100010], OYPK_cnt, deep[100010], siz[100010], top[100010];

struct EDGE{
    int y, nex;
}edge[200010];

int n, m, s, mo;

void add(int x, int y){
    edge[++OYPK_cnt].y=y;
    edge[OYPK_cnt].nex=head[x];
    head[x]=OYPK_cnt;
}

void dfs1(int x, int fat, int dep){
    deep[x]=dep;
    fa[x]=fat;
    siz[x]=1;
    int max_son=-1;
    for(int i=head[x]; i; i=edge[i].nex){
        int y=edge[i].y;
        if(y!=fat){
            dfs1(y, x, dep+1);
            if(siz[y]>max_son){
                max_son=siz[y];
                son[x]=y;
            }
            siz[x]+=siz[y];
        }
    }
}

void dfs2(int x, int hea){
    id[x]=++cnt;
    nw[cnt]=w[x];
    top[x]=hea;
    if(!son[x]){
        return;
    }
    dfs2(son[x], hea);
    for(int i=head[x]; i; i=edge[i].nex){
        int y=edge[i].y;
        if(y!=fa[x]&&y!=son[x]){
            dfs2(y, y);
        }
    }
}

void push_down(int l, int r, int rt){
    if(laz[rt]!=0){
        laz[rt<<1]+=laz[rt];
        laz[rt<<1|1]+=laz[rt];
        int mid=(l+r)>>1;
        sum[rt<<1]+=laz[rt]*(mid-l+1);
        sum[rt<<1|1]+=laz[rt]*(r-mid);
        sum[rt<<1]%=mo;
        sum[rt<<1|1]%=mo;
        laz[rt]=0;
    }
}
void build(int l, int r, int rt){
    if(l==r){
        sum[rt]=nw[l]%mo;
        return;
    }
    int mid=(l+r)>>1;
    build(l, mid, rt<<1);
    build(mid+1, r, rt<<1|1);
    sum[rt]=(sum[rt<<1]+sum[rt<<1|1])%mo;
}

int query(int a, int b, int l, int r, int rt){
    if(a<=l&&r<=b){
        return sum[rt];
    }
    push_down(l, r, rt);
    int mid=(l+r)>>1, now_ans=0;
    if(a<=mid){
        now_ans+=query(a, b, l, mid, rt<<1);
    }
    if(b>mid){
        now_ans+=query(a, b, mid+1, r, rt<<1|1);
    }
    now_ans%=mo;
    return now_ans;
}


void update(int a, int b, int l, int r, int rt, int jia){
    if(a<=l&&r<=b){
        laz[rt]+=jia;
        sum[rt]+=jia*(r-l+1);
        return;
    }
    push_down(l, r, rt);
    int mid=(l+r)>>1;
    if(a<=mid){
        update(a, b, l, mid, rt<<1, jia);
    }
    if(b>mid){
        update(a, b, mid+1, r, rt<<1|1, jia);
    }
    sum[rt]=(sum[rt<<1]+sum[rt<<1|1])%mo;
}

int q_bian(int x, int y){
    int now_ans=0;
    while(top[x]!=top[y]){
        if(deep[top[x]]<deep[top[y]]){
            swap(x, y);
        }
        now_ans+=query(id[top[x]], id[x], 1, n, 1);
        now_ans%=mo;
        x=fa[top[x]];
    }
    if(deep[x]>deep[y]){
        int t=x;
        x=y;
        y=t;
    }
    now_ans+=query(id[x], id[y], 1, n, 1);
    now_ans%=mo;
    return now_ans;
}

void up_bian(int x, int y, int jia){
    jia%=mo;
    while(top[x]!=top[y]){
        if(deep[top[x]]<deep[top[y]]){
            swap(x, y);
        }
        update(id[top[x]], id[x], 1, n, 1, jia);
        x=fa[top[x]];
    }
    if(deep[x]>deep[y]){
        swap(x, y);
    }
    update(id[x], id[y], 1, n, 1, jia);
}

int q_son(int x){
    int now_ans=query(id[x], id[x]+siz[x]-1, 1, n, 1);
    now_ans%=mo;
    return now_ans;
}

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

int main(){
    //freopen("a.out", "w", stdout);
    scanf("%d%d%d%d", &n, &m, &s, &mo);
    for(int i=1; i<=n; i++){
        scanf("%d", &w[i]);
    }
    for(int i=1; i<=n-1; i++){
        int x, y;
        scanf("%d%d", &x, &y);
        add(x, y);
        add(y, x);
    }
    dfs1(s, 0, 1);
    dfs2(s, s);
    build(1, n, 1);
    for(int i=1; i<=m; i++){
        int flag;
        scanf("%d", &flag);
        if(flag==1){
            int x, y, z;
            scanf("%d%d%d", &x, &y, &z);
            up_bian(x, y, z);
            continue;
        }
        if(flag==2){
            int x, y;
            scanf("%d%d", &x, &y);
            printf("%d\n", q_bian(x, y));
            continue;
        }
        if(flag==3){
            int x, y;
            scanf("%d%d", &x, &y);
            up_son(x, y);
            continue;
        }
        if(flag==4){
            int x;
            scanf("%d", &x);
            printf("%d\n", q_son(x));
        }
    }
}

后记

鸣谢

洛谷(提供题目)

关于作者

初一蒟蒻,天天被大佬虐。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值