【进阶】树链剖分

之前写过一个基本的树链剖分,但是我今天要讲的进阶版树链剖分要能支持以下操作:

  • 换根:将一个指定的节点设置为树的新根。
  • 修改路径权值:给定两个节点,将这两个节点间路径上的所有节点权值(含这两个节点)增加一个给定的值。
  • 修改子树权值:给定一个节点,将以该节点为根的子树内的所有节点权值增加一个给定的值。
  • 询问路径:询问某条路径上节点的权值和。
  • 询问子树:询问某个子树内节点的权值和。

其实还是板子题

其他的前置知识:树状数组支持区间修改区间查询(单纯图个方便),不会的话可以看这里


说说进阶其实也没什么东西。。。

相较于我原来写的 blog 只多了修改询问子树和换根的操作,但不得不说换根操作是真的妙,值得讲一讲。

先将换根吧。假设原来的根是 r t rt rt ,新的根是 n e w r t newrt newrt 。那么换根代码只有一行 r t = n e w r t rt = newrt rt=newrt

这下有人要喷了,这不什么都没干嘛。说得对,确实什么都没干,但是也很好理解,树链剖分没有 LCT 那么灵活,每次换根重构整棵树的话时间上实在支撑不起。

但是由于这个原因,修改询问子树就比原来要复杂一些了,修改和询问大致都可以分为三种情况,这里以修改为例:

  • x = r o o t x = root x=root

    从意义上理解,整棵树的权值都要修改,直接调用树状数组的 u p d a t e update update 就可以了

  • 在原树中( r o o t = 1 root = 1 root=1时) x x x r o o t root root 的祖先,判断条件是 x x x d f s dfs dfs 序中有 t i d x ≤ t i d r o o t tid_x \leq tid_{root} tidxtidroot t e d x ≥ t e d r o o t ted_x \geq ted_{root} tedxtedroot 同时成立,其中 t i d x tid_x tidx 表示 x x x d f s dfs dfs 序中第一次出现的位置, t e d x ted_x tedx 表示 x x x d f s dfs dfs 序中最后一次出现的位置。

    这种情况比较复杂,具体情况还是要自己画图,我在这里口头阐述一下。

    其实画几个图就能发现这么一个东西,这种情况下修改子树权值的范围是整棵树除去一棵子树。至于那个子树的根节点嘛,,还是要画图(因为我也是画了一大堆图才明白的,要是在电脑上画这个时间开销实在太大了,但是你想背板子其实关系不大,因为代码特别清晰)。

    假如你真的去画图了,你会发现这么一个规律,这棵被 lou 掉的子树的根 t t t 要么是 x x x 的重儿子,要么是 x x x 旁边的一个轻儿子(是 r o o t root root 在原树中的祖先),而到底是谁取决于 r o o t root root 蹦到 x x x 所在的重链上是否和 x x x 重合。如果不是,即为前者,反之为后者。

  • 其他情况,易得其实换不换根没有影响,直接当原来的版本做就可以了。


背板大法好

class BinaryIndexedTree {
private:
    long long c[2][N];
    inline int lowbit(int x) { return x & (-x); }
    inline void update(int x, long long v) {
        for (int i = x; i <= n; i += lowbit(i)) c[0][i] += v, c[1][i] += x * v;
    }
    inline long long query(int x) {
        long long ans = 0;
        for (int i = x; i > 0; i -= lowbit(i)) ans += (x + 1) * c[0][i] - c[1][i];
        return ans;
    }
public:
    inline void update(int l, int r, long long v) { update(l, v); update(r + 1, -v); }
    inline long long query(int l, int r) { return query(r) - query(l - 1); }
} BIT;
class TreeChain {
private:
    int root, parent[N], size[N], depth[N], son[N], top[N], tid[N], ted[N];
    inline void dfsPre(int u, int p, int d) {
        parent[u] = p, depth[u] = d, size[u] = 1;
        for (int i = head[u]; i; i = edge[i].next) {
            int v = edge[i].to;
            if (v == p) continue;
            dfsPre(v, u, d + 1);
            size[u] += size[v];
            if (size[v] > size[son[u]]) son[u] = v;
        }
    }
    inline void dfsTop(int u, int tp) {
        static int dfsClock = 0;
        top[u] = tp; tid[u] = ++dfsClock;
        if (son[u]) dfsTop(son[u], tp);
        for (int i = head[u]; i; i = edge[i].next) {
            int v = edge[i].to;
            if (v == parent[u] || v == son[u]) continue;
            dfsTop(v, v);
        }
        ted[u] = dfsClock;
    }
    inline int reach(int x, int y) {
        int ans = 0;
        while (top[x] != top[y]) {
            ans = top[x];
            x = parent[top[x]];
        }
        return x == y ? ans : son[y];
    }
public:
    inline void init(int rt) { root = rt; dfsPre(root, 0, 1); dfsTop(root, root); }
    inline void changeRoot(int rt) { root = rt; }
    inline void updateInit(int x, int v) { BIT.update(tid[x], tid[x], v); }
    inline void updateRoute(int x, int y, int z) {
        while (top[x] != top[y]) {
            if (depth[top[x]] < depth[top[y]])
                std::swap(x, y);
            BIT.update(tid[top[x]], tid[x], z);
            x = parent[top[x]];
        }
        if (depth[x] > depth[y])
            std::swap(x, y);
        BIT.update(tid[x], tid[y], z);
    }
    inline long long queryRoute(int x, int y) {
        long long ans = 0;
        while (top[x] != top[y]) {
            if (depth[top[x]] < depth[top[y]])
                std::swap(x, y);
            ans += BIT.query(tid[top[x]], tid[x]);
            x = parent[top[x]];
        }
        if (depth[x] > depth[y])
            std::swap(x, y);
        return ans + BIT.query(tid[x], tid[y]);
    }
    inline void updateSubtree(int x, int z) {
        if (x == root)
            BIT.update(1, n, z);
        else if (tid[x] <= tid[root] && ted[x] >= ted[root]) {
            int t = reach(root, x);
            BIT.update(1, tid[t] - 1, z);
            BIT.update(ted[t] + 1, n, z);
        } else
            BIT.update(tid[x], ted[x], z);
    }
    inline long long querySubtree(int x) {
        if (x == root)
            return BIT.query(1, n);
        else if (tid[x] <= tid[root] && ted[x] >= ted[root]) {
            int t = reach(root, x);
            return BIT.query(1, tid[t] - 1) + BIT.query(ted[t] + 1, n);
        } else
            return BIT.query(tid[x], ted[x]);
    }
};

必须压压行了,否则这个码量堪称恐怖,但其实仔细观察其中的一半都是可以 Ctrl + C 和 Ctrl + V 解决的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值