树刨板子--[HAOI2015]树上操作

在这里插入图片描述

对于树刨而言,简单来说就是将树形问题转变成序列问题。
在这里插入图片描述

对于常见的几种刨的形式基本上都是使用的重链刨分或者 长链刨分,两者使用起来感觉差距不算特别大。

尝试优美写代码的第 X day,感觉写起来已经变成了奇怪的形状了。(其实还是格式化的)

感觉刨的过程中有一个log, 之后上树状数组又有一个log。不知道是不是写法问题。

#include <bits/stdc++.h>
using i64 = long long;
int main()
{
    std::cin.tie(nullptr)->std::ios::sync_with_stdio(false);
    int n, m, cnt = 0;
    std::cin >> n >> m;
    std::vector<i64> tr1(n + 2), tr2(n + 2), sz(n + 2), son(n + 2), top(n + 2), dfn(n + 2), idfn(n + 2), s(n + 2), fa(n + 2), out(n + 2);
    std::vector<std::vector<int>> node(n + 1);
    std::function<void(std::vector<i64> &, int, i64)> add = [&](std::vector<i64> &tr, int x, i64 c) -> void
    {
        for (; x <= n; x += x & -x){
            tr[x] += c;
        }
    };
    std::function<i64(std::vector<i64> &, int)> ask = [&](std::vector<i64> &tr, int x) -> i64
    {
        i64 ans = 0;
        for (; x; x &= x - 1){
            ans += tr[x];
        }
        return ans;
    };
    std::function<void(int, int)> insert = [&](int x, int c) -> void
    {
        add(tr1, x, c);
        add(tr2, x, 1LL * x * c);
    };
    std::function<i64(int)> query = [&](int x) -> i64
    {
        return ask(tr1, x) * (x + 1) - ask(tr2, x);
    };
    std::function<void(int, int)> dfs1 = [&](int u, int v) -> void
    {
        sz[u] = 1;
        fa[u] = v;
        for (int x : node[u]){
            if (x != v)
            {
                dfs1(x, u);
                sz[u] += sz[x];
                if (sz[x] > sz[son[u]])
                {
                    son[u] = x;
                }
            }
        }
    };
    std::function<void(int, int)> dfs2 = [&](int u, int to) -> void
    {
        top[u] = to;
        dfn[u] = ++cnt;
        idfn[cnt] = u;
        if (son[u])
        {
            dfs2(son[u], to);
            for (int x : node[u]){
                if (x != fa[u] && x != son[u]){
                    dfs2(x, x);
                }
            }
        }
        out[u] = cnt;
    };
    for (int i = 1; i <= n; i++)
    {
        std::cin >> s[i];
    }
    for (int i = 2; i <= n; i++)
    {
        int u, v;
        std::cin >> u >> v;
        node[u].push_back(v);
        node[v].push_back(u);
    }
    dfs1(1, 0);
    dfs2(1, 1);
    for (int i = 1; i <= n; i++)
    {
        insert(dfn[i], s[i]);
        insert(dfn[i] + 1, -s[i]);
    }
    for (int i = 1; i <= m; i++)
    {
        i64 op, x, a = 0;
        std::cin >> op;
        switch (op)
        {
        case 1:
            std::cin >> x >> a;
            insert(dfn[x], a);
            insert(dfn[x] + 1, -a);
            break;
        case 2:
            std::cin >> x >> a;
            insert(dfn[x], a);
            insert(out[x] + 1, -a);
            break;
        case 3:
            std::cin >> x;
            while (x)
            {
                int r = dfn[x], l = dfn[top[x]];
                x = fa[top[x]];
                a += query(r) - query(l - 1);
            }
            std::cout << a << "\n";
            break;
        default:
            assert(false);
            break;
        }
    }
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值