树链剖分

树链剖分

分为重链剖分和长链剖分,

这篇博客现只谈论重链剖分 我不会告诉你我还不会长链剖分 QAQ

大概操作就是通过两层Dfs将一棵树剖为数个链,再用线段树处理,可将树形结构变为线型结构,便于操作。 也就是优雅地暴力

一般可用于处理一些树上问题,比如LCA

上代码


#define combo void
#define eakag bool
#define eafoo int

#define fa(x) tree[x].fa
#define id(x) tree[x].id
#define dep(x) tree[x].dep
#define siz(x) tree[x].siz
#define son(x) tree[x].son
#define top(x) tree[x].top
struct SLPF{
    struct Edge
    {
        int dep, siz, son, top, fa, id;
    }tree[maxn];
    int cnt;
    /*
    dfs1
    1.标记每个点的深度
    2.标记每个点的父亲
    3.标记每个非叶子节点的子树的大小 
    4.标记每个非叶子节点的重儿子编号
    */
    combo Dfs1(int u, int faa, int deep)
    {
        dep(u) = deep, fa(u) = faa, siz(u) = 1;
        int maxson = -1;
        for(int i = head[u]; i; i = e[i].next)
        {
            int v = e[i].to;
            if(v == faa) continue;
            Dfs1(v, u, deep + 1);
            siz(u) += siz(v);
            if(siz(v) > maxson)
                son(u) = v, maxson = siz(v);
        }
        return;
    }
    /*
    1.标记每个点的新编号
    2.赋值每个点的初始值到新编号上
    3.处理每个点所在链的顶端
    4.处理每条链
    */
    combo Dfs2(int u, int topf)
    {//topf指当前链的最顶端节点
        id(u) = ++cnt,//标记每个节点的新编号
        top(u) = topf,//这个点所在链的顶端
        at[cnt] = a[u];//把每个点的初始值赋给新编号
        if(! son(u))return;
        Dfs2(son(u), topf);
        for(int i = head[u]; i; i = e[i].next)
        {
            int v = e[i].to;
            if(v == fa(u) || v == son(u)) continue;
            Dfs2(v, v);
        }
        return;
    }

    eafoo get_lca(int x, int y)
    {
        while(top(x) != top(y))
        {
            if(dep(top(x)) < dep(top(y)))
                swap(x, y);
            x = fa(top(x));
        }
        return dep(x) > dep(y) ? y : x;
    }
    
    combo Updata_Range(int x, int y, int k)
    {//将节点x到y节点的最短路径上的点的权值均加k    
        while(top(x) != top(y))
        {
            if(dep(top(x)) < dep(top(y)))
                swap(x, y);
            T.Updata(1, 1, n, id(top(x)), id(x), k);
            x = fa(top(x));
        }
        if(dep(x) > dep(y)) swap(x,y);
        T.Updata(1, 1, n, id(x), id(y), k);
        return;
    }

    combo Updata_val(int x, int k)
    {
        return (combo)(T.updata(1, 1, n, id(x), k));
    }

    combo Updata_Son(int x, int k)
    {//将以节点x为根节点的子树上的点的权值均加k
        return (combo)(T.Updata(1, 1, n, id(x), id(x) + siz(x) - 1, k));
    }

    eafoo Query_Range(int x, int y)
    {//求节点x到节点y上所有点的权值之和
        int ans = 0;
        while(top(x) != top(y))
        {
            if(dep(top(x)) < dep(top(y)))
                swap(x,y);
            ans += T.Query(1, 1, n, id(top(x)), id(x));
            x = fa(top(x));
        }
        if(dep(x) > dep(y)) swap(x,y);
        ans += T.Query(1, 1, n, id(x), id(y));
        return ans;
    }  

    eafoo Query_Son(int x)
    {//求以x为根节点的子树的权值之和
        return T.Query(1, 1, n, id(x), id(x) + siz(x) - 1);
    }
}S;



剩下的就是个线段树的板子

#define combo void
#define eakag bool
#define eafoo int

#define val(x) S_tree[x].val
#define lazy(x) S_tree[x].lazy
struct Segment_tree
{
    struct segment_tree
    {
        int val, lazy;
    }S_tree[maxn];

    inline combo pushup(int rt)
    {
        return (combo)(val(rt) = (val(rt << 1) + val(rt << 1|1)));
    }

    inline combo pushdown(int rt, int l, int r)
    {
        if(lazy(rt))
        {
            int mid = (l + r) >> 1; 
            lazy(rt << 1) += lazy(rt),
            lazy(rt << 1|1) += lazy(rt);
            val(rt << 1) += lazy(rt) * (mid - l + 1),
            val(rt << 1|1) += lazy(rt)*(r - mid);
            lazy(rt) = 0;
        }
        return;
    }

    combo Build(int rt, int l, int r)
    {
        if(l == r)
        {
            val(rt) = at[l];
        }
        else{
            int mid = (l + r) >> 1;
            Build(rt<<1, l, mid);
            Build(rt<<1|1, mid + 1, r);
            pushup(rt);
        }
        return;
    }

    combo updata(int rt, int l, int r, int k, int val)
    {
        if(l == r)
        {
            val(rt) += val;
            return;
        }
        else
        {
            pushdown(rt, l, r);
            int mid = (l + r) >> 1;
            if(k <= mid)
                 updata(rt << 1, l, mid, k, val);
            else updata(rt << 1 | 1, mid + 1, r, k, val);
            pushup(rt);
            return;
        }
    }

    combo Updata(int rt, int l, int r, int L, int R, int k){
        if(L <= l && r <= R)
        {
            lazy(rt) += k, val(rt) += k * (r - l + 1);
        }
        else
        {
            pushdown(rt, l, r);
            int mid = (l + r) >> 1;
            if(L <= mid) Updata(rt << 1, l, mid, L, R, k);
            if(R > mid) Updata(rt << 1|1, mid + 1, r, L, R, k);
            pushup(rt);
        }
        return;
    }

    eafoo Query(int rt, int l, int r, int L, int R)
    {
        if(L <= l && r <= R)
        {
            return val(rt);
        }
        else
        {
            pushdown(rt, l, r);
            int mid = (l + r) >> 1, ans = 0;
            if(L <= mid) ans += Query(rt << 1, l, mid, L, R);
            if(R > mid) ans += Query(rt << 1|1, mid + 1, r, L, R);
            return ans;            
        }
    }
}T;

例题 : luogu P3384 【模板】轻重链剖分/树链剖分

posted @ 2022-07-02 11:19  Eakang  阅读(33)  评论(0编辑  收藏  举报