点分树

拿板子说。
先考虑只询问一次的情况,直接点分治就行。
但是多次询问就不行了,时间复杂度会爆炸,所以我们考虑一个新的算法————淀粉鼠!

建树方法就是把每一次找到的重心连到上一次找到的重心上,形成一棵树,具体来说就是先对于整棵树找重心,然后把这个重心和它连的边全删了,再对这棵树上剩下的每个部分找重心,以此类推,就建好了。

那这棵树有什么优点

  • 这棵树的树高是 \(\log n\) 的,也就是说对于每个点我们暴力跳父亲最多跳 \(O(\log)\) 次就跳到根了。
  • 这棵树上任意两个点 \(x\)\(y\) ,它们的 \(lca\) 为重心划分子连通块时 \(x,y\) 首次分割开,所以说明这个点一定在原树中 \(x\)\(y\) 的路径上
  • 而对于一个 \(x\) ,它在点分树上的 \(lca\) 最多有 \(\log\) 个,所以计算 \(y\) 的话一般用容斥的方法计算出来

来看本题。

\(f_1(u,j)\) 表示在 \(u\) 子树中与 \(u\) 距离小于等于 \(j\) 的点的权值和, \(f_2(u,j)\) 表示在 \(u\) 父亲的子树中与 \(u\) 距离小于等于 \(j\) 的点的权值和

那么在一次查询 \((x,k)\) 中,一对虚树上的父子节点的贡献就是:

\[f(i,fa_i) = f_1(fa_i,k-dis(x,fa_i)) - f_2(i,k - dis(x, fa_i)) \]

不难发现的是有贡献的这一对父子节点只能在要查询节点 \(x\) 的上面,也就是只能是 \(x\) 的祖先,所以这一次查询的答案就是:

\[ans(x,k) = f_1(x,k) + \sum_{i\in fatree(x),fa_i\neq 0}f(i,fa_i) \]

需要注意的是,\(x\) 自己子树里的贡献需要单独算

维护 \(f_1,f_2\) 可以动态开点,不难实现。

贴个代码

#include <vector>
#include <iostream>

using namespace std;
const int N = 1e6 + 10;
const int inf = 1e9 + 7;

int n, m, rt, sum, tot;
int a[N];
int head[N];
bool vis[N];
struct Map { int to, nxt; } e[N << 1];

void add (int u, int v) {
    e[++tot] = {v, head[u]};
    head[u] = tot;
}

namespace LCA {
    int dep[N], sz[N], son[N], fa[N], top[N];

    void dfs1 (int u, int f) {
        sz[u] = 1, fa[u] = f;
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if (v == f) continue;
            dep[v] = dep[u] + 1;
            dfs1 (v, u), sz[u] += sz[v];
            if (sz[son[u]] < sz[v]) son[u] = v;
        }
    }

    void dfs2 (int u, int topf) {
        top[u] = topf;
        if (son[u]) dfs2 (son[u], topf);
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if (v == fa[u] || v == son[u]) continue;
            dfs2 (v, v);
        }
    }

    int Lca (int x, int y) {
        while (top[x] != top[y]) {
            if (dep[top[x]] < dep[top[y]]) std::swap(x, y);
            x = fa[top[x]];
        }
        return dep[x] < dep[y] ? x : y;
    }

    int dis (int x, int y) {
        return dep[x] + dep[y] - 2 * dep[Lca (x, y)];
    }
}

struct BIT {
    int n;
    vector <int> sum;

    int lb (int x) { return x & (-x); }

    void build (int m) { sum.resize ((n = m) + 2); }

    void add (int x, int k) {
        x++;
        for (int i = x; i <= n; i += lb(i)) 
            sum[i] += k;
        return;
    }

    int ask (int x) {
        int res = 0; x++;
        x = min (x, n);
        for (int i = x; i; i -= lb(i))
            res += sum[i];
        return res;
    }
} t1[N], t2[N];

int fa[N], sz[N], mxs[N];

void getrt (int u, int fa) {
    sz[u] = 1, mxs[u] = 0;
    for (int i = head[u]; i; i = e[i].nxt) {
        int v = e[i].to;
        if (v == fa || vis[v]) continue;
        getrt (v, u), sz[u] += sz[v];
        mxs[u] = max (mxs[u], sz[v]);
    }
    mxs[u] = max (mxs[u], sum - mxs[u]);
    if (mxs[u] < mxs[rt]) rt = u;
}

void solve (int x, int f) {
    int now = sum;
    t1[x].build (now + 1), t2[x].build (now + 1);
    vis[x] = true, fa[x] = f;
    for (int i = head[x]; i; i = e[i].nxt) {
        int v = e[i].to;
        if (vis[v]) continue;
        sum = (sz[v] > sz[x] ? now - sz[x] : sz[v]);
        mxs[rt = 0] = inf;
        getrt(v, 0), solve (rt, x);
    }
}

void modify (int x, int k) {
    t1[x].add (0, k);
    for (int i = x; fa[i]; i = fa[i]) {
        int d = LCA::dis(x, fa[i]);
        t1[fa[i]].add (d, k);
        t2[i].add (d, k);
    }
}

int query (int x, int k) {
    int res = t1[x].ask (k);
    for (int i = x; fa[i]; i = fa[i]) {
        int d = LCA::dis (fa[i], x);
        if (d > k) continue;
        res += t1[fa[i]].ask (k - d) - t2[i].ask (k - d);
    }
    return res;
}

int main () {
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    for (int i = 2, u, v; i <= n; ++i)
        cin >> u >> v, add (u, v), add (v, u);
    LCA::dfs1 (1, 0), LCA::dfs2 (1, 1);
    sum = n, mxs[rt = 0] = inf;
    getrt (1, 0), solve (rt, 0);
    for (int i = 1; i <= n; i++)
	    modify (i, a[i]);
    for (int i = 1, opt, x, y, lastans = 0; i <= m; i++) {
	    cin >> opt >> x >> y;
	    x ^= lastans; y ^= lastans;
	    if (opt == 1) modify (x, y - a[x]), a[x] = y;
	    else lastans = query (x, y), cout << lastans << '\n';
	}
	return 0;
}
posted @ 2025-05-10 14:39  Rose_Lu  阅读(42)  评论(2)    收藏  举报