[模板] 点分树
前言
确实是树上问题中一个非常神奇的部分
好好学一下
思路
点分树的性质其实还是比较好理解, 这里重点解释点分树的两棵段树是怎么回事, 具体如何维护
首先要知道的是, 维护两棵段树的目的是为了去除同子树中的贡献
贡献计算(查询)
- 表示点分树上的父子关系
- 表示点 在点分树上的子树集合
- 表示点 在点分树上的父树集合
- 表示点 和点 在点分树上的距离
- 表示点 的点权
其中
- 式表示重构树上 的子树中与 距离小于等于 的点权值之和
- 式表示重构树上 的子树中与 距离小于等于 的点权值之和
如何利用?
假设当前查询  , 即查询所有距离  小于等于  的点权和
        对于重构树上的父子节点  ,  即除了  子树外面的部分 对答案的贡献为
        
解释一下, 我们考虑只在重构树上的  处统计  的贡献一个性质是 重构树上的  一定是  的必经之路, 也就是说如果枚举  , 那么满足条件的  必定满足 
        不难发现  , 考虑枚举  , 借以统计答案
        上面计算的  的意义就是枚举  ,  的贡献
但是还是有问题, 不难发现如果 在 的同一个子树之中, 那么其 必然不等于 , 那么 的贡献就会重复, 所以需要用以下操作去重
-  :
 表示重构树上点 的子树集合中, 距离 小于等于 的点权和
-  :
 发现上一个式子包含了两点同在一个子树内的情况, 所以需要减去重构树上点 的子树中, 距离 小于等于 的点权和
最终的答案就是
        
贡献更新(修改)
考虑修改的情况, 其实跟上面很像, 按照定义修改  和 
        具体怎么做?
对于这个点你往上暴力跳 , 然后更新即可
所以我们现在需要维护单点修改, 询问前缀, 不难发现可以用动态开点线段树处理
用树链剖分维护 \(\rm{LCA}\) 即可
代码
#include <bits/stdc++.h>
const int MAXN = 1e5 + 20;
int n, m;
int w[MAXN];
/*动态开点线段树*/
struct DSTree {
    struct node { int ls, rs; int val; node() { ls = rs = val = 0; } }; std::vector<node> Tree;
    int cnt, rt;
    DSTree() { cnt = 0, rt = 0; }
    /*----------------------------------------*/
    void pushup(int rt) { Tree[rt].val = Tree[Tree[rt].ls].val + Tree[Tree[rt].rs].val; }
    // void pushdown()
    // void addtag()
    /*----------------------------------------*/
    void build(int sz) { Tree.resize((sz + 1) * 6); }
    void update(int& rt, int l, int r, int val, int aim) {
        if (!rt) rt = ++cnt;
        if (l == r) { Tree[rt].val += val; return; }
        int mid = (l + r) >> 1;
        if (aim <= mid) update(Tree[rt].ls, l, mid, val, aim);
        else update(Tree[rt].rs, mid + 1, r, val, aim);
        pushup(rt);
    }
    int query(int rt, int l, int r, int L, int R) {
        if (!rt) return 0;
        if (L <= l && r <= R) return Tree[rt].val;
        int mid = (l + r) >> 1, ans = 0;
        if (L <= mid) ans += query(Tree[rt].ls, l, mid, L, R);
        if (R > mid) ans += query(Tree[rt].rs, mid + 1, r, L, R);
        return ans;
    }
};
/*树*/
struct graph {
    struct node { int to, nxt; } edge[MAXN << 1]; int head[MAXN], cnt = -1;
    void head_init() { memset(head, -1, sizeof head); }
    void addedge(int u, int v) { edge[++cnt] = (node) { v, head[u] }; head[u] = cnt; }
    int tmp; // dfn 辅助
    graph() { tmp = 0, head_init(); }
    struct point {
        DSTree f, g;
        int dep, fa, siz, Hson, dfn, top;
        int mxt;
    } dot[MAXN];
} tree, retr;
/*树链剖分*/
class TreeDivder {
private:
    void dfs1(int u, int fat) {
        tree.dot[u].dep = tree.dot[fat].dep + 1, tree.dot[u].fa = fat, tree.dot[u].siz = 1;
        for (int e = tree.head[u]; ~e; e = tree.edge[e].nxt) {
            int v = tree.edge[e].to; if (v == fat) continue;
            dfs1(v, u); tree.dot[u].siz += tree.dot[v].siz;
            if (!tree.dot[u].Hson || tree.dot[v].siz > tree.dot[tree.dot[u].Hson].siz) tree.dot[u].Hson = v;
        }
    }
    void dfs2(int u, int topu) {
        tree.dot[u].dfn = ++tree.tmp, tree.dot[u].top = topu;
        if (tree.dot[u].Hson) dfs2(tree.dot[u].Hson, topu);
        for (int e = tree.head[u]; ~e; e = tree.edge[e].nxt) {
            int v = tree.edge[e].to; if (v == tree.dot[u].fa || v == tree.dot[u].Hson) continue;
            dfs2(v, v);
        }
    }
public:
    void init(int rt) { tree.dot[rt].dep = 1; dfs1(rt, 0); dfs2(rt, rt); }
    int LCA(int u, int v) {
        while (tree.dot[u].top != tree.dot[v].top) {
            /*让链头更深的先跳*/ if (tree.dot[tree.dot[u].top].dep < tree.dot[tree.dot[v].top].dep) std::swap(u, v);
            u = tree.dot[tree.dot[u].top].fa;
        }
        return (tree.dot[u].dep < tree.dot[v].dep) ? u : v;
    }
    int dis(int u, int v) { return tree.dot[u].dep + tree.dot[v].dep - 2 * tree.dot[LCA(u, v)].dep; }
} diver;
/*重构树*/
class TreeRestructer {
private:
    int done[MAXN], sum; // 已经处理完的部分
public:
    TreeRestructer(int x) { sum = x; memset(done, false, sizeof done); }
    /*获取该连通块的重心*/
    void getrt(int u, int fat, int& rt) {
        tree.dot[u].siz = 1, tree.dot[u].mxt = 0;
        for (int e = tree.head[u]; ~e; e = tree.edge[e].nxt) {
            int v = tree.edge[e].to; if (v == fat || done[v]) continue;
            getrt(v, u, rt); tree.dot[u].siz += tree.dot[v].siz;
            tree.dot[u].mxt = std::max(tree.dot[u].mxt, tree.dot[v].siz);
        }
        tree.dot[u].mxt = std::max(tree.dot[u].mxt, sum - tree.dot[u].siz);
        if (tree.dot[u].mxt <= tree.dot[rt].mxt || !rt) rt = u;
    }
    /*建立重构树*/
    void build(int rt, int fat) {
        done[rt] = true; int subsize = sum;
        retr.dot[rt].fa = fat;
        retr.dot[rt].f.build(subsize / 2 + 1), retr.dot[rt].g.build(subsize + 1);
        retr.dot[rt].siz = subsize;
        for (int e = tree.head[rt]; ~e; e = tree.edge[e].nxt) {
            int v = tree.edge[e].to; if (done[v]) continue;
            sum = tree.dot[v].siz > tree.dot[rt].siz ? subsize - tree.dot[rt].siz : tree.dot[v].siz;
            int x; tree.dot[x = 0].mxt = 0x3f3f3f3f;
            getrt(v, rt, x); build(x, rt);
        }
    }
};
/*处理问题*/
class AnsGiver {
private:
    void modify(int idx, int val) {
        int now = idx;
        while (~now) {
            int fa = retr.dot[now].fa;
            retr.dot[now].f.update(retr.dot[now].f.rt, 0, retr.dot[now].siz, val, diver.dis(now, idx));
            if (~fa) retr.dot[now].g.update(retr.dot[now].g.rt, 0, retr.dot[fa].siz, val, diver.dis(fa, idx));
            now = fa;
        }
    }
    int query(int idx, int k) {
        int res = 0;
        int now = idx, last = 0;
        while (~now) {
            int d = diver.dis(idx, now);
            if (d > k) { last = now, now = retr.dot[now].fa; continue; } // 注意是 continue
            res += retr.dot[now].f.query(retr.dot[now].f.rt, 0, retr.dot[now].siz, 0, k - d);
            if (last) res -= retr.dot[last].g.query(retr.dot[last].g.rt, 0, retr.dot[now].siz, 0, k - d);
            last = now, now = retr.dot[now].fa;
        }
        return res;
    }
public:
    int lastans = 0;
    AnsGiver() { lastans = 0; }
    void decode(int& x, int& y) { x ^= lastans, y ^= lastans; }
    void solve() {
        for (int i = 1; i <= n; i++) modify(i, w[i]);
        for (int i = 1, op, x, y; i <= m; i++) {
            scanf("%d %d %d", &op, &x, &y); decode(x, y);
            if (op) { modify(x, y - w[x]); w[x] = y; }
            else { printf("%d\n", lastans = query(x, y)); }
        }
    }
} ag;
int main()
{
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%d", &w[i]);
    tree.head_init(); for (int i = 1, u, v; i < n; i++) scanf("%d %d", &u, &v), tree.addedge(u, v), tree.addedge(v, u);
    /*先处理树链剖分*/ diver.init(1);
    /*建立重构树*/ TreeRestructer tr(n); int rt; tree.dot[rt = 0].mxt = 0x3f3f3f3f; tr.getrt(1, -1, rt), tr.build(rt, -1);
    ag.solve();
    return 0;
}
不知道 \(\rm{MLE}\) 是什么造成的, 后面再看

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号