思路:一个很显然的操作是把
w
−
d
i
s
(
x
,
y
)
w-dis(x,y)
w−dis(x,y)写成
w
+
d
e
p
x
+
d
e
p
y
−
2
d
e
p
l
c
a
(
x
,
y
)
w+dep_x+dep_y-2dep_{lca(x,y)}
w+depx+depy−2deplca(x,y)
前三项都能用一个变量累加出来,那么主要问题就是如何较快的求
d
e
p
l
c
a
(
x
,
y
)
dep_{lca(x,y)}
deplca(x,y)。
那我们可以对树进行轻重链剖分处理,对每条链维护两个bit。一个维护个数,一个维护dep的和。
每个update操作,从操作点开始往根跳,对沿途经过的链在bit上更新一下。
如下图所示:假设我们当前在4号点所在的链,我们先更新一下这条链的信息;
那么接下来我们会跳到10号点所在的链,再更新一下链的信息。
求答案的时候,我们也是跳链来累加答案,需要注意的是,在计算个数的时候有可能会重复计算,需要减掉重复的值。
可以结合代码来理解一下。。。
至于点分树的做法明天在学一下
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 5e4 + 10;
#define fi first
#define se second
#define pb push_back
#define wzh(x) cerr<<#x<<'='<<x<<endl;
int t, n, m, cnt;
int siz[N], fa[N], de[N], to[N], a[N], son[N];
vector<int>v[N];
void dfs1(int now, int pre, int d) {
siz[now] = 1;
fa[now] = pre;
de[now] = d;
int cnt = -1;
for (auto k : v[now]) {
if (k == pre)continue;
dfs1(k, now, d + 1);
siz[now] += siz[k];
if (siz[k] > cnt) {
cnt = siz[k];
son[now] = k;
}
}
}
vector<int>link[N];
void dfs2(int now, int pre) {
to[now] = pre;
a[now] = ++cnt;
link[pre].pb(now);
if (!son[now])return ;
dfs2(son[now], pre);
for (auto k : v[now]) {
if (k == fa[now] || k == son[now])continue;
dfs2(k, k);
}
}
LL laz[N];
int num[N];
vector<LL>bit[N], ti[N];
void add(int x, int y) {
int z = ti[x].size();
for (; y < z; y += y & -y)ti[x][y]++;
}
int ge(int x, int y) {
int z = 0;
for (; y > 0; y -= y & -y)z += ti[x][y];
return z;
}
void add(int x, int y, int w) {
int z = bit[x].size();
for (; y < z; y += y & -y)bit[x][y] += w;
}
LL get(int x, int y) {
LL z = 0;
for (; y > 0; y -= y & -y) {
z += bit[x][y];
}
return z;
}
void gao(int l){
while (l) {
add(to[l], num[l], de[l]);//维护dep和
add(to[l], num[l]);//维护个数
l = fa[to[l]];
}
}
LL get(int l) {
int pre = 0;
LL res = 0;
while (l) {
res += 1ll * (ge(to[l], num[l]) - pre ) * de[l];//bit上到当前点的前缀和(维护个数的) 去掉重复的
pre = ge(to[l], num[to[l]]);//下一次计算的时候重复的显然就是此是这个个链内出现的总数
if (l != to[l]) {
res += get(to[l], num[to[l]]) - get(to[l], num[l]);//当前点到top节点的 dep之和
}
l = fa[to[l]];
}
return res;
}
LL ans[N];
int main() {
for (scanf("%d", &t); t; t--) {
scanf("%d%d", &n, &m); cnt = 0;
for (int i = 1; i <= n; i++) {
v[i].clear();
laz[i] = 0;
ans[i] = 0;
link[i].clear();
son[i] = 0;
}
for (int i = 1; i < n; i++) {
int x, y;
scanf("%d%d", &x, &y);
v[x].pb(y);
v[y].pb(x);
}
dfs1(1, 0, 0);
dfs2(1, 1);
for (int i = 1; i <= n; i++) {
int x = link[i].size();
for (int j = 0; j < x; j++) {
num[link[i][j]] = x - j;
}
bit[i].resize(x + 1);
ti[i].resize(x + 1);
for (int j = 0; j <= x; j++) {
bit[i][j] = ti[i][j] = 0;
}
}
LL w = 0;
LL dep = 0;
int cou = 0;
for (int i = 1; i <= m; i++) {
int ty;
scanf("%d", &ty);
if (ty == 1) {
int dx, dw;
scanf("%d%d", &dx, &dw);
w += dw;
cou++;
dep += de[dx];
gao(dx);
} else if (ty == 2) {
int dx;
scanf("%d", &dx);
LL res = get(dx);
res = w - (dep + 1ll * cou * de[dx] - 2 * res);
if (res + ans[dx] <= 0) {
continue;
} else {
ans[dx] = -(res);//小于0,那么ans必然是等于此时的-res
}
} else {
int dx;
scanf("%d", &dx);
LL res = get(dx);
res = w - (dep + 1ll * cou * de[dx] - 2 * res);
printf("%lld\n", res + ans[dx]);
}
}
}
return 0;
}