树链剖分

树链剖分用于解决树上操作问题,大致是 DFS序+线段树,没有看过的可以先了解一下 。

树剖把树中的儿子节点分为重儿子和轻儿子,树中链分为重链和轻链,目的是加快操作速率,降低时间复杂度。树剖的精华之处类似于LCA,跳来跳去提升效率,而且能做LCA做不到的功能,数组开了那么多还是很有用的。

几个概念:

  • 重儿子:子树中节点最多的儿子节点
  • 轻儿子:非重儿子
  • 重链:由重儿子连成的节点
  • 轻链:非重链

几个数组:

用下面的数组保存树的信息,具体的作用看了代码就明白了

  • depth[] : 节点深度
  • heavy_son[] : 节点的重儿子
  • father[] : 节点的父亲
  • size[] : 节点的子树数量
  • id[] : 所有节点的dfs序
  • rk[] : 节点在dfs序(id[])中的位置
  • top[] : 每个节点所在链的顶端节点

需要两次dfs操作,第一次得出 每个节点的 depth, heavy_son, father, size

void dfs1(int u, int fa) ///size,heavy_son,depth,father
{
    siz[u] = 1;
    heavy_son[u] = 0;
    for(int i=head[u]; i ; i=nxt[i]){
        int v = to[i];
        if(v == fa) continue;
        depth[v] = depth[u] + 1;
        father[v] = u;
        dfs1(v, u);
        siz[u] += siz[v];
        if(siz[v] > siz[heavy_son[u]])
            heavy_son[u] = v;
    }
}

第二次得出每个节点的 id, rk, top

void dfs2(int u, int rt) ///id,rk,top
{
    id[u] = ++Index;
    rk[Index] = u;
    top[u] = rt;
    if(heavy_son[u]) dfs2(heavy_son[u], rt);
    for(int i=head[u]; i; i=nxt[i]){
        int v = to[i];
        if(v == father[u] || v == heavy_son[u]) continue;
        dfs2(v, v);
    }
}

这时候树的基本信息就被保存好了,接下来就是对树疯狂操作,修改查询什么的。现在的信息维护工作就通过DFS序交给线段树来处理了,把对链的查询修改操作转化成了区间修改查询操作。以查找最大值为例,视情况不同稍作修改。

int Query(int u, int v)
{
    int res = -inf;
    while(top[u] != top[v]){
        if(depth[top[u]] < depth[top[v]]) swap(u, v); 
        res = max(res, query(1, 1, n, id[top[u]], id[u])); //线段树的查询函数
        u = father[top[u]];
    }
    if(depth[u] < depth[v]) swap(u, v);
    res = max(res, query(1, 1, n, id[v], id[u]));
    return res;
}


几个题:

P2590 [ZJOI2008]树的统计——https://www.luogu.org/problemnew/show/P2590

单点修改、要维护最大值和区间和

#include<bits/stdc++.h>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
const int maxn = 1e5+5;
const int inf = 0x7fffffff;
int head[maxn], nxt[maxn], to[maxn];
int n, a[maxn], sum[maxn<<2], Max[maxn<<2], q, cnt, Index;
int siz[maxn], heavy_son[maxn], father[maxn], depth[maxn], top[maxn], rk[maxn], id[maxn];
void addedge(int u, int v)
{
    to[++cnt] = v;
    nxt[cnt] = head[u];
    head[u] = cnt;
}
void dfs1(int u, int fa) ///size,heavy_son,depth,father
{
    siz[u] = 1;
    heavy_son[u] = 0;
    for(int i=head[u]; i ; i=nxt[i]){
        int v = to[i];
        if(v == fa) continue;
        depth[v] = depth[u] + 1;
        father[v] = u;
        dfs1(v, u);
        siz[u] += siz[v];
        if(siz[v] > siz[heavy_son[u]])
            heavy_son[u] = v;
    }
}
void dfs2(int u, int rt) ///id,rk,top
{
    id[u] = ++Index;
    rk[Index] = u;
    top[u] = rt;
    if(heavy_son[u]) dfs2(heavy_son[u], rt);
    for(int i=head[u]; i; i=nxt[i]){
        int v = to[i];
        if(v == father[u] || v == heavy_son[u]) continue;
        dfs2(v, v);
    }
}
void pushup(int rt)
{
    sum[rt] = sum[lson] + sum[rson];
    Max[rt] = max(Max[lson], Max[rson]);
}
void build(int rt, int l, int r)
{
    if(l == r){
        sum[rt] = Max[rt] = a[rk[l]];
        return ;
    }
    int mid = (l+r)>>1;
    build(lson, l, mid);
    build(rson, mid+1, r);
    pushup(rt);
}
void update(int rt, int l, int r, int x, int val)
{
    if(l == r){
        sum[rt] = Max[rt] = val;
        return ;
    }
    int mid = (l+r)>>1;
    if(x <= mid) update(lson, l, mid, x, val);
    else update(rson, mid+1, r, x, val);
    pushup(rt);
}
int query(int rt, int l, int r, int L, int R, bool flag)
{
    if(L <= l && R >= r) return flag ? Max[rt] : sum[rt];
    int mid = (l+r)>>1, ans;
    if(flag){
        ans = -inf;
        if(L <= mid) ans = max(ans, query(lson, l, mid, L, R, flag));
        if(R > mid) ans = max(ans, query(rson, mid+1, r, L, R, flag));
    }else {
        ans = 0;
        if(L <= mid) ans += query(lson, l, mid, L, R, flag);
        if(R > mid) ans += query(rson, mid+1, r, L, R, flag);
    }
    pushup(rt);
    return ans ;
}
int Query(int u, int v, bool flag)
{
    int res = flag ? -inf : 0;
    while(top[u] != top[v]){
        if(depth[top[u]] < depth[top[v]]) swap(u, v);
        if(flag) res = max(res, query(1, 1, n, id[top[u]], id[u], flag));
        else res += query(1, 1, n, id[top[u]], id[u], flag);
        u = father[top[u]];
    }
    if(depth[u] < depth[v]) swap(u, v);
    if(flag) res = max(res, query(1, 1, n, id[v], id[u], flag));
    else res += query(1, 1, n, id[v], id[u], flag);
    return res;
}
int main()
{
    cin >> n;
    int x, y;
    for(int i=1; i<n; i++){
        cin >> x >> y;
        addedge(x, y);
        addedge(y, x);
    }
    for(int i=1; i<=n; i++) cin >> a[i];
    depth[1] = 1; father[1] = 1; dfs1(1, 0); dfs2(1, 1);
    build(1, 1, n);
    cin >> q;
    for(int i=1; i<=q; i++){
        string str;
        cin >> str >> x >> y;
        if(str[0] == 'C'){
            update(1, 1, n, id[x], y);
        }else if(str[1] == 'M'){
            cout << Query(x, y, 1) << endl;
        }else {
            cout << Query(x, y, 0) << endl;
        }
    }
}

P3384 【模板】树链剖分——https://www.luogu.org/problemnew/show/P3384

多了区间修改,修改x的子树就是修改dfs序中区间 [id[x],id[x]+size[x]-1]

#include<bits/stdc++.h>
#define LL long long
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
const int maxn = 5e5+5;
LL head[maxn], nxt[maxn], to[maxn];
LL n, a[maxn], sum[maxn<<2], q, m, lazy[maxn], r, p, cnt, Index;
LL siz[maxn], heavy_son[maxn], father[maxn], depth[maxn], top[maxn], rk[maxn], id[maxn];
void addedge(int u, int v)
{
    to[++cnt] = v;
    nxt[cnt] = head[u];
    head[u] = cnt;
}
void dfs1(int u, int fa) ///size,heavy_son,depth,father
{
    siz[u] = 1;
    heavy_son[u] = 0;
    for(int i=head[u]; i ; i=nxt[i]){
        int v = to[i];
        if(v == fa) continue;
        depth[v] = depth[u] + 1;
        father[v] = u;
        dfs1(v, u);
        siz[u] += siz[v];
        if(siz[v] > siz[heavy_son[u]])
            heavy_son[u] = v;
    }
}
void dfs2(int u, int rt) ///id,rk,top
{
    id[u] = ++Index;
    rk[Index] = u;
    top[u] = rt;
    if(heavy_son[u]) dfs2(heavy_son[u], rt);
    for(int i=head[u]; i; i=nxt[i]){
        int v = to[i];
        if(v == father[u] || v == heavy_son[u]) continue;
        dfs2(v, v);
    }
}

void pushup(int rt)
{
    sum[rt] = (sum[lson] + sum[rson])%p;
}
void pushdown(int rt, int len)
{
    if(lazy[rt]!=0){
        lazy[lson] = (lazy[lson] + lazy[rt])%p;
        lazy[rson] = (lazy[rson] + lazy[rt])%p;
        sum[lson] = (sum[lson] + lazy[rt]*(len-(len>>1)))%p;
        sum[rson] = (sum[rson] + lazy[rt]*(len>>1))%p;
        lazy[rt] = 0;
    }
}
void build(int rt, int l, int r)
{
    lazy[rt] = 0;
    if(l == r){
        sum[rt] = a[rk[l]]%p;
        return ;
    }
    pushdown(rt, r-l+1);
    int mid = (l+r)>>1;
    build(lson, l, mid);
    build(rson, mid+1, r);
    pushup(rt);
}
void update(int rt, int l, int r, int L, int R, int val)
{
    if(L <= l && R >= r) {
        sum[rt] = (sum[rt]+val*(r-l+1))%p;
        lazy[rt] = (lazy[rt] + val)%p;
        return ;
    }
    pushdown(rt, r-l+1);
    int mid = (l+r)>>1;
    if(L <= mid) update(lson, l, mid, L, R, val);
    if(R > mid) update(rson, mid+1, r, L, R, val);
    pushup(rt);
}
int query(int rt, int l, int r, int L, int R)
{
    if(L <= l && R >= r) return sum[rt]%p ;
    pushdown(rt, r-l+1);
    int mid = l+r>>1; LL res=0;
    if(L <= mid) res = (res + query(lson, l, mid, L, R))%p;
    if(R > mid) res = (res + query(rson, mid+1, r, L, R))%p;
    return res%p;
}
void Update(int u, int v, int val)
{
    while(top[u] != top[v]){
        if(depth[top[u]] < depth[top[v]]) swap(u, v);
        update(1, 1, n, id[top[u]], id[u], val);
        u = father[top[u]];
    }
    if(depth[u] < depth[v]) swap(u, v);
    update(1, 1, n, id[v], id[u], val);
}
int Query(int u, int v)
{
    LL res = 0;
    while(top[u] != top[v]){
        if(depth[top[u]] < depth[top[v]]) swap(u, v);
        res = (res + query(1, 1, n, id[top[u]], id[u]))%p;
        u = father[top[u]];
    }
    if(depth[u] < depth[v]) swap(u, v);
    res = (res + query(1, 1, n, id[v], id[u]))%p;
    return res%p;
}
int main()
{
    cin >> n >> m >> r >> p;
    for(int i=1; i<=n; i++) cin >> a[i];
    for(int i=1; i<n; i++){
        int x, y;
        cin >> x >> y;
        addedge(x, y); addedge(y, x);
    }
    depth[r] = 1; father[r] = r; dfs1(r, 0); dfs2(r, r);
    build(1, 1, n);
    for(int i=1; i<=m; i++){
        int x, y, z, op;
        cin >> op;
        if(op == 1){
            cin >> x >> y >> z;
            Update(x, y, z);
        }else if(op == 2){
            cin >> x >> y;
            cout << Query(x, y) << endl;
        }else if(op == 3){
            cin >> x >> z;
            update(1, 1, n, id[x], id[x]+siz[x]-1, z);
        }else {
            cin >> x;
            cout << query(1, 1, n, id[x], id[x]+siz[x]-1) << endl;
        }
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值