点分治学习Thinking

Typing SVG

点分树学习

Front

看我的上一篇文章!

What?

实际上就是淀粉质的动态(?)版(吧?)

Bing

根据\(\tt Bing\)说,

点分树就是把点分治时“每一层的重心”“下一层的重心”连起来,构成的一棵新树。这棵树的深度被强行压缩到了 \(O(\log n)\)

好像看不懂?

What??

换句话说吧

fast 函数里,你每次找到重心 c 后,不要只是处理完就走,而是记录一下:他的父亲

  • 第一层的重心作为点分树的根。
  • 第二层的重心(子树重心)连向第一层的重心。
  • 依次类推。

How?

我们手玩发现,虽然在点分树中,谁是爸爸被打乱了 怎么有点像我们的小学 但他们的距离不变。

比如 \((u, v)\) 谁是父亲不重要了,但距离永远是\(dist(u, v)\)!!!

根据淀粉质可得,每一次向下遍历都会砍掉一半,所以高度为\(\log n\)

所以如果点 \(u\) 的值改变了话,修改他的\(fa[u]\)\(fa[fa[u]]\)吗,一直到根节点。就可以了!!!

Why?

为啥要建这棵树?

为什么不能在原树上修改?

如果原树是一条 \(10^5\) 长度的链,你修改叶子节点,影响到的祖先有 \(10^5\) 个,复杂度退化为 \(O(n)\)。 但在点分树中,任何一个点到根的距离不超过 20 层(\(\log_2 10^5 \approx 17\))。好精美!!!!🎇🎇😮😮

总复杂度: \(O(q \log^2 n)\) 豪爽豪爽豪爽豪爽豪爽!

Q&A

为什么要用树状数组?😋😋

点分树把树的深度压到了 \(O(\log n)\),让我们能快速找到受影响的祖先。但每个祖先节点手里都管着一堆距离它远近不一的点。当你问“距离 \(x\)\(K\) 以内”时,这其实是一个前缀和(或者说区间和)查询。 BIT 就是那个能以 \(O(\log (范围))\) 的速度处理这种“前缀和”的最快工具。

实际上,(ZKW)线段树也可以,无非只是常数大了点

为什么要用LCA

在点分树上“爬楼梯”时,我们频繁需要知道一个点 \(u\) 到它点分树祖先 \(anc\) 之间的原始树距离 \(dist(u, anc)\)。虽然点分树把树结构变了,但题目要求的距离永远是原树上的距离。

而计算原树上两点距离的标准公式是:

\[dist(u, v) = depth[u] + depth[v] - 2 \times depth[LCA(u, v)] \]

所以,没有 LCA,你就无法在点分树上进行任何关于“距离”的计算。

Code

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 100010;
vector<int> G[MAXN];
int val[MAXN], temp_val[MAXN];
int n, m;

//SZ2
class BIT
{
private:
    vector<int> bits;
    inline int lowbit(int x) { return x & -x; }

public:
    void init(int n) { bits.assign(n + 5, 0); }
    void add(int x, int v)
    {
        x++;
        if (x <= 0)
            return;
        for (int i = x; i < (int)bits.size(); i += lowbit(i))
            bits[i] += v;
    }
    int query(int x)
    {
        x++;
        if (x <= 0)
            return 0;
        if (x >= (int)bits.size())
            x = (int)bits.size() - 1;
        int ans = 0;
        for (int i = x; i > 0; i -= lowbit(i))
            ans += bits[i];
        return ans;
    }
} T0[MAXN], T1[MAXN];

// ST & RMQ & LCA
int dep[MAXN], st[MAXN << 1][21], pos[MAXN], dfn_cnt, lg2[MAXN << 1];
void dfs_lca(int u, int f, int d)
{
    st[++dfn_cnt][0] = u;
    pos[u] = dfn_cnt;
    dep[u] = d;
    for (int v : G[u])
    {
        if (v != f)
        {
            dfs_lca(v, u, d + 1);
            st[++dfn_cnt][0] = u;
        }
    }
}

void init_st()
{
    lg2[1] = 0;
    for (int i = 2; i <= dfn_cnt; i++)
        lg2[i] = lg2[i / 2] + 1;
    for (int j = 1; j <= 20; j++)
        for (int i = 1; i + (1 << j) - 1 <= dfn_cnt; i++)
        {
            int a = st[i][j - 1], b = st[i + (1 << (j - 1))][j - 1];
            st[i][j] = (dep[a] < dep[b] ? a : b);
        }
}

int get_dist(int u, int v)
{
    int l = pos[u], r = pos[v];
    if (l > r)
        swap(l, r);
    int k = lg2[r - l + 1];
    int a = st[l][k], b = st[r - (1 << k) + 1][k];
    int lca = (dep[a] < dep[b] ? a : b);
    return dep[u] + dep[v] - 2 * dep[lca];
}

// main
int sz[MAXN], maxs[MAXN], root, tot_sz, pa[MAXN];
bool vis[MAXN];

void get_sz(int u, int f)
{
    sz[u] = 1;
    for (int v : G[u])
    {
        if (v != f && !vis[v])
        {
            get_sz(v, u);
            sz[u] += sz[v];
        }
    }
}

void get_root(int u, int f)
{
    sz[u] = 1;
    maxs[u] = 0;
    for (int v : G[u])
    {
        if (v != f && !vis[v])
        {
            get_root(v, u);
            sz[u] += sz[v];
            maxs[u] = max(maxs[u], sz[v]);
        }
    }
    maxs[u] = max(maxs[u], tot_sz - sz[u]);
    if (maxs[u] < maxs[root])
        root = u;
}

void build(int u, int f, int s)
{
    vis[u] = true;
    pa[u] = f;
    T0[u].init(s);
    T1[u].init(s);

    for (int v : G[u])
    {
        if (!vis[v])
        {
            get_sz(v, 0);
            tot_sz = sz[v];
            root = 0;
            maxs[0] = MAXN;
            get_root(v, 0);
            int next_root = root;
            build(next_root, u, tot_sz);
        }
    }
}

void update(int u, int x)
{
    int delta = x - val[u];
    val[u] = x;
    for (int i = u; i; i = pa[i])
    {
        T0[i].add(get_dist(u, i), delta);
        if (pa[i])
            T1[i].add(get_dist(u, pa[i]), delta);
    }
}

int query(int u, int k)
{
    int res = T0[u].query(k);
    for (int i = u; pa[i]; i = pa[i])
    {
        int d = get_dist(u, pa[i]);
        if (k >= d)
        {
            res += T0[pa[i]].query(k - d);
            res -= T1[i].query(k - d);
        }
    }
    return res;
}

// solve
void solve()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        cin >> temp_val[i];

    for (int i = 1; i < n; i++)
    {
        int u, v;
        cin >> u >> v;
        G[u].push_back(v);
        G[v].push_back(u);
    }

    dfs_lca(1, 0, 0);
    init_st();

    root = 0;
    maxs[0] = MAXN;
    tot_sz = n;
    get_root(1, 0);
    int first_root = root;
    build(first_root, 0, n);

    for (int i = 1; i <= n; i++)
        update(i, temp_val[i]);

    int lastans = 0;
    while (m--)
    {
        int opt, x, y;
        cin >> opt >> x >> y;
        x ^= lastans;
        y ^= lastans;
        if (opt == 0)
        {
            lastans = query(x, y);
            cout << lastans << "\n";
        }
        else
        {
            update(x, y);
        }
    }
}

signed main(void)
{
    // freopen("name.in", "r", stdin);
    // freopen("name.out", "w", stdout);
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int T = 1;
    // cin >> T;
    while (T--)
        solve();
    return 0;
}

完结撒花🎉🎉

posted @ 2026-01-09 20:23  cn_xpr  阅读(8)  评论(0)    收藏  举报