树链剖分入门学习

 

dfs序:A B D G H I C E J F

用处?

就是把树强行搞成了“连续的”

我们可以发现两个重要的性质:

一个结点的子树上的结点的时间戳,一定大于这个结点的时间戳且连续

某些链上的时间戳也是连续的

那么对于操作3,4,就可以套一个线段树实现了

把树看成数组,时间戳是下标,结点的值为当前下标的值

a[1] = 'A', a[2] = 'B', a[7] = 'C'

操作3:将以x为根结点的子树内所有结点值都加上z

操作4:求以x为根节点的子树内所有结点值值和

那么,操作1和操作2怎么搞呢?并不是所有链都是连续的时间戳啊

这时候我们就需要树链剖分大法

 

 ​​​​​​​然后利用线段树解决问题

开始剖分:

跑一边dfs,标记以下内容:

        结点的父亲是谁

        结点的重儿子是谁

        结点的深度

        结点的大小

        可是没有标记时间戳啊,因为我们要先知道结点的重儿子是谁,才能开始标记时间戳,所以呢,再跑一遍dfs,标记以下内容;

                结点权值的dfs序与时间戳

                当前结点所在重链的头是谁,头的头就是自己

const int maxn = 1e5 + 5;
const int maxm = maxn * 2;
struct E{
    int to, next;
}Edge[maxm];
int tot, Head[maxn];//Head数组初始化为-1而不是0
inline void AddEdge(int u, int v){
    Edge[tot] = (E){v, Head[u]};
    Head[u] = tot++;
    Edge[tot] = (E){u, Head[v]};
    Head[v] = tot++;
}
int fa[maxn], dep[maxn], siz[maxn], son[maxn];
void dfs1(int u, int f){
    fa[u] = f;
    dep[u] = dep[f] + 1;
    siz[u] = 1;
    int maxsize = -1;//判断是不是重儿子的临时变量
    for(int i = Head[u]; ~i; i = Edge[i].next){
        int v = Edge[i].to;
        if(v == f) continue;
        dfs1(v, u);
        siz[u] += siz[v];
        if(siz[v] > maxsize){
            maxsize = siz[v];
            son[u] = v;
        }
    }
}
int tim, dfn[maxn], top[maxn],w[maxn], v[maxn];
void dfs2(int u, int t){
    dfn[u] = ++tim;
    top[u] = t;
    w[tim] = v[u];
    if(!son[u]) return;
    dfs2(son[u], t);
    for(int i = Head[u]; ~i; i = Edge[i].next){
        int v = Edge[i].to;
        if(v == fa[u] || v == son[u]) continue;
        dfs2(v, v);
    }
}

 线段树:就不写了

操作3:将以x为根节点的子树内所有结点值都加上z

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

inline void mson(int x, int z){
    modify(dfn[x], dfn[x] + siz[x] - 1, z);
}
inline int qson(int x){
    return query(dfn[x], dfn[x] + siz[x] - 1);
}

引理:除了根结点外的任何一个结点的父亲结点都一定在一条重链上

证明:因为父亲结点存在儿子,所以一定存在重儿子,所以一定在一条重链上

 

 

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
const int maxm = maxn * 2;
int v[maxn];
struct E
{
    int to, next;
} Edge[maxm];
int tot, Head[maxn];
inline void AddEdge(int u, int v)
{
    Edge[tot] = (E){v, Head[u]};
    Head[u] = tot++;
    Edge[tot] = (E){u, Head[v]};
    Head[v] = tot++;
}
int mod;
int fa[maxn], dep[maxn], siz[maxn], son[maxn];
void dfs1(int u, int f)
{
    fa[u] = f;
    dep[u] = dep[f] + 1;
    siz[u] = 1;
    int maxsize = -1;
    for (int i = Head[u]; ~i; i = Edge[i].next)
    {
        int v = Edge[i].to;
        if (v == f)
            continue;
        dfs1(v, u);
        siz[u] += siz[v];
        if (siz[v] > maxsize)
        {
            maxsize = siz[v];
            son[u] = v;
        }
    }
}
int tim, dfn[maxn], top[maxn], w[maxn];
void dfs2(int u, int t){
    dfn[u] = ++tim;
    top[u] = t;
    w[tim] = v[u];
    if(!son[u]) return;
    dfs2(son[u], t);
    for(int i = Head[u]; ~i; i = Edge[i].next){
        int v = Edge[i].to;
        if(v == fa[u] || v == son[u]) continue;
        dfs2(v, v);
    }
}
struct node{
    int l, r, f, val;
}sgt[maxn * 4];
inline int ls(int k){return k << 1;}
inline int rs(int k){return k << 1 | 1;}
inline void pushup(int k){ sgt[k].val = (sgt[ls(k)].val + sgt[rs(k)].val) % mod;}
inline void pushdown(int k){
    sgt[ls(k)].f += sgt[k].f;
    sgt[rs(k)].f += sgt[k].f;
    sgt[ls(k)].val += (sgt[ls(k)].r - sgt[ls(k)].l + 1) * sgt[k].f % mod;
    sgt[rs(k)].val += (sgt[rs(k)].r - sgt[rs(k)].l + 1) * sgt[k].f % mod;
    sgt[k].f = 0;
}
void build(int l, int r, int k = 1){
    sgt[k].l = l, sgt[k].r = r;
    if(l == r){
        sgt[k].val = w[l] % mod;
        return;
    }
    int m = (l + r) >> 1;
    build(l, m, ls(k));
    build(m + 1, r, rs(k));
    pushup(k);
}
void modify(int x, int y, int z, int k = 1){
    int l = sgt[k].l, r = sgt[k].r;
    if(x <= l && y >= r){
        sgt[k].f += z;
        sgt[k].val += (r - l + 1) * z;
        sgt[k].val %= mod;
        return;
    }
    if(sgt[k].f) pushdown(k);
    int m = (l + r) >> 1;
    if(x <= m) modify(x, y, z, ls(k));
    if(y > m) modify(x, y, z, rs(k));
    pushup(k);
}
int query(int x, int y, int k = 1){
    int l = sgt[k].l, r = sgt[k].r;
    if(x <= l && y >= r) return sgt[k].val;
    if(sgt[k].f) pushdown(k);
    int sum = 0, m = (l + r) >> 1;
    if(x <= m) sum += query(x, y, ls(k));
    if(y > m) sum += query(x, y, rs(k));
    return sum % mod;
}
inline void mson(int x, int z){
    modify(dfn[x], dfn[x] + siz[x] - 1, z);
}
inline int qson(int x){
    return query(dfn[x], dfn[x] + siz[x] - 1);
}
void mchain(int x, int y, int z){
    z %= mod;
    while(top[x] != top[y]){
        if(dep[top[x]] < dep[top[y]]) swap(x, y);
        modify(dfn[top[x]], dfn[x], z);
        x = fa[top[x]];
    }
    if(dep[x] > dep[y]) swap(x, y);
    modify(dfn[x], dfn[y], z);
}
int qchain(int x, int y){
    int ret = 0;
    while(top[x] != top[y]){
        if(dep[top[x]] < dep[top[y]]) swap(x, y);
        ret += query(dfn[top[x]], dfn[x]);
        x = fa[top[x]];
    }
    if(dep[x] > dep[y]) swap(x, y);
    ret += query(dfn[x], dfn[y]);
    return ret % mod;
}
void print(int x){
    cout << x << "\n";
}
int main(){
    memset(Head, -1, sizeof(Head));
    int n, m, r;
    cin >> n >> m >> r >> mod;
    for(int i = 1; i <= n; i++){
        cin >> v[i];
    }
    for(int i = 1; i < n; i++){
        int u, v;
        cin >> u >> v;
        AddEdge(u, v);
    }
    
    dfs1(r, r);
    dfs2(r, r);
    build(1, n);
    while(m--){
        int opt, x, y ,z;
        cin >> opt;
        switch(opt){
            case 1:
                cin >> x >> y >> z;
                mchain(x, y ,z);
                break;
            case 2:
                cin >> x >> y;
                print(qchain(x, y));
                break;
            case 3:
                cin >> x >> z;
                mson(x, z);
                break;
            case 4:
                cin >> x;
                print(qson(x));
                break;
        }
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值