对于树刨而言,简单来说就是将树形问题转变成序列问题。
对于常见的几种刨的形式基本上都是使用的重链刨分或者 长链刨分,两者使用起来感觉差距不算特别大。
尝试优美写代码的第 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;
}
}
}