【模版】树(重)链剖分


树(重)链剖分


已知一棵包含 N N N 个结点的树,每个节点上包含一个数值,需要支持以下操作:

操作 1 1 1: 格式: 1 1 1 x x x y y y z z z 表示将树从 x x x y y y 结点最短路径上所有节点的值都加上 z z z

操作 2 2 2: 格式: 2 2 2 x x x y y y 表示求树从 x x x y y y 结点最短路径上所有节点的值之和

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

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


一、算法前置知识点
1. 1. 1.树剖法求树上 L C A LCA LCA.
2. 2. 2.线段树的区间运用.

二、算法流程
1. 1. 1. 做好树剖法求 L C A LCA LCA 的预处理工作
① ① d f s 1 dfs1 dfs1 ② ② d f s 2 dfs2 dfs2
树剖法求LCA(与本文相连接)
2. 2. 2. 1. 1. 1.中的 ② ② 里:
添加一个 b a s [ i ] bas[i] bas[i] 数组,记录树的重链剖分法的 d f s dfs dfs 序 。
即在 d f s 2 dfs2 dfs2 里 首行 添加 b a s [ + + b a s [ 0 ] ] = x bas[++bas[0]] = x bas[++bas[0]]=x ( b a s [ 0 ] ) (bas[0]) (bas[0])仅起计数作用。
通过此数组,我们可以得到用剖分法剖树得到的序列。
这样,该树的每一条链里的点在这个序列里都是连续的!
因此可以通过 该序列建立一棵线段树,对原来的树里的链的点 可以进行 区间操作。
3. 3. 3. 建立线段树:
线段树(与此文相连接)
考虑根据什么建立一棵线段树?
我们得到的 b a s [ i ] bas[i] bas[i] 数组 只是 按链的点号顺序的序列。我们需要 记录每个点号 出现在 序列的第几个位置。这样才能实现线段树的区间修改。
再建立一个数组 p o s [ b a s [ i ] ] = i pos[bas[i]]=i pos[bas[i]]=i 表示 点号为 b a s [ i ] bas[i] bas[i]的点 在序列中位于 i i i 号。

for(int i = 1; i <= n; i++) pos[bas[i]] = i;

然后 在对 b a s [ i ] bas[i] bas[i] 序列 建立一颗线段树,线段树保存的 每个点号对应的权值。

tree[cur] = f[bas[l]];

其余线段树内容不变。
4. 4. 4. 查询和修改的变化:
在修改 x x x y y y路径上的点的权值时,由于是树剖法求 L C A LCA LCA
一定有一个点从 当 前 点 当前点 跳至 当 前 点 的 链 头 的 父 亲 所 在 点 当前点的链头的父亲 所在点
而途径的所有点,在 b a s [ i ] bas[i] bas[i] 是连续的,我们通过 p o s [ i ] pos[i] pos[i] 已经确定了这些点在线段树中是连续的,就可以用线段树对这些点统一做 修 改 修改 操作。
而同理查询:
在查询 x x x y y y路径上的点的权值和时,由于是树剖法求 L C A LCA LCA
一定有一个点从 当 前 点 当前点 跳至 当 前 点 的 链 头 的 父 亲 所 在 点 当前点的链头的父亲 所在点
而途径的所有点,在 b a s [ i ] bas[i] bas[i] 是连续的,我们通过 p o s [ i ] pos[i] pos[i] 已经确定了这些点在线段树中是连续的,就可以用线段树对这些点统一做 求 和 求和 操作。

而查询或修改 x x x 点子树的点的权值和时,由于 d f s dfs dfs 序的性质,点 x x x 与 其子树上的点 在 b a s [ i ] bas[i] bas[i] 数组中也是连续的。
因此可以用线段树做修改和查询操作。变化参照上边的查询和修改。
查询和查询的范围是 [ p o s [ x ] , p o s [ x ] + s i z e [ x ] − 1 ] [pos[x],pos[x]+size[x]-1] [pos[x],pos[x]+size[x]1]

完整代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdlib>
using namespace std;
#define N 100010
#define M 100010
int n,m,s,p,ans;
int f[N],head[N],tree[N<<2],bas[N],top[N],lazy[N<<2],pos[N];
int deep[N],size[N],fa[N],son[N];
struct list
{
    int to,nxt;
}e[M<<1];
int read()
{
    int rt = 0, in = 1; char ch = getchar();
    while(ch < '0' || ch > '9') {if(ch == '-') in = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {rt = rt * 10 + ch - '0'; ch = getchar();}
    return rt * in;
}
void add_edge(int u, int v)
{
    e[++head[0]].to = v;
    e[head[0]].nxt = head[u];
    head[u] = head[0];
}
int dfs1(int x)
{
    size[x] = 1;
    for(int i = head[x]; i; i = e[i].nxt)
    {
        if(deep[e[i].to])   continue;
        deep[e[i].to] = deep[x] + 1;
        fa[e[i].to] = x;
        size[x] += dfs1(e[i].to);
        if(size[son[x]] < size[e[i].to])    son[x] = e[i].to;
    }
    return size[x];
}
void dfs2(int x, int root)
{
    bas[++bas[0]] = x, top[x] = root;
    if(son[x])  dfs2(son[x], root);
    for(int i = head[x]; i; i = e[i].nxt)
        if(e[i].to != son[x] && e[i].to != fa[x])
            dfs2(e[i].to, e[i].to);
}
void build_tree(int cur, int l, int r)
{
    if(l == r)
    {
        tree[cur] = f[bas[l]];
        return;
    }
    int mid = l + r >> 1;
    build_tree(cur<<1, l, mid);
    build_tree(cur<<1|1, mid+1, r);
    tree[cur] = (tree[cur<<1] + tree[cur<<1|1]) % p;
}
void pushdown(int cur, int l, int r)
{
    int mid = l + r >> 1;
    tree[cur<<1] = (tree[cur<<1] + (mid-l+1) * lazy[cur]) % p;
    tree[cur<<1|1] = (tree[cur<<1|1] + (r-mid) * lazy[cur]) % p;
    lazy[cur<<1] += lazy[cur];
    lazy[cur<<1|1] += lazy[cur];
    lazy[cur] = 0;
}
void update(int cur, int l, int r, int L, int R, int w)
{
    if(L <= l && r <= R)
    {
        tree[cur] = (tree[cur] + (r-l+1) * w) % p;
        lazy[cur] += w;
        return;
    }
    if(lazy[cur])   pushdown(cur, l, r);
    int mid = l + r >> 1;
    if(R <= mid)    update(cur<<1, l, mid, L, R, w);
    else if(L > mid)    update(cur<<1|1, mid+1, r, L, R, w);
    else    update(cur<<1, l, mid, L, mid, w), update(cur<<1|1, mid+1, r, mid+1, R, w);
    tree[cur] = (tree[cur<<1] + tree[cur<<1|1]) % p;
}
void query_sum(int cur, int l, int r, int L, int R)
{
    if(L <= l && r <= R)
    {
        ans = (ans + tree[cur]) % p;
        return;
    }
    if(lazy[cur])   pushdown(cur, l, r);
    int mid = l + r >> 1;
    if(R <= mid)    query_sum(cur<<1, l, mid, L, R);
    else if(L > mid)    query_sum(cur<<1|1, mid+1, r, L, R);
    else    query_sum(cur<<1, l, mid, L, mid), query_sum(cur<<1|1, mid+1, r, mid+1, R);
    tree[cur] = tree[cur<<1] + tree[cur<<1|1];
}
void query_lca(int u, int v, int w, int ins)
{
    while(top[u] != top[v])
    {
        if(deep[top[u]] > deep[top[v]]) swap(u, v);
        if(ins == 1)    update(1, 1, n, pos[top[v]], pos[v], w);
        if(ins == 2)    query_sum(1, 1, n, pos[top[v]], pos[v]);
        v = fa[top[v]];
    }
    if(deep[u] > deep[v])   swap(u, v);
    if(ins == 1)    update(1, 1, n, pos[u], pos[v], w);
    if(ins == 2)    query_sum(1, 1, n, pos[u], pos[v]);
}
int main()
{
    n = read(), m = read(), s = read(), p = read();
    for(int i = 1; i <= n; i++) f[i] = read();
    for(int i = 1; i < n; i++)
    {
        int u = read(), v = read();
        add_edge(u,v), add_edge(v,u);
    }
    deep[s] = 1;
    dfs1(s);
    dfs2(s, s);
    for(int i = 1; i <= n; i++) pos[bas[i]] = i;
    build_tree(1, 1, n);
    for(int i = 1; i <= m; i++)
    {
        int ins = read();
        if(ins == 1)
        {
            int l = read(), r = read(), w = read();
            query_lca(l, r, w, 1);
        }
        else if(ins == 2)
        {
            ans = 0;
            int l = read(), r = read();
            query_lca(l, r, 0, 2);
            printf("%d\n",ans%p);
        }
        else if(ins == 3)
        {
            int x = read(), w = read();
            update(1, 1, n, pos[x], pos[x]+size[x]-1, w);
        }
        else if(ins == 4)
        {
            ans = 0;
            int x = read();
            query_sum(1, 1, n, pos[x], pos[x]+size[x]-1);
            printf("%d\n",ans%p);
        }
    }
    system("pause");
    return 0;
} 

总结:重链剖分 = 树剖求LCA + 线段树 + bas[i] + pos[i]
例题:
[模版]重链剖分
luogu P3258
luogu P2680

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值