P6626 [省选联考 2020 B 卷] 消息传递 题解
一眼点分治。
设当前分治中心为 $u$,正在考虑子树 $V$ 的贡献。
则 $\forall v\in V$,此次分治对询问 $v,k$ 的贡献为 $\sum\limits_{i\notin V}[d_i=k-d_v]$。
用桶维护。具体地,先把 $u$ 子树内所有点的深度装进桶里,
考虑 $V$ 的贡献时把 $V$ 内所有点的深度拿出来,统计 $\forall v\in V$ 询问 $v,k$ 的贡献,再把 $V$ 内所有点的深度装回去。
询问 $u,k$ 的贡献单独计算,为 $\sum[d_i=k]$。
#include <cstdio>
#include <vector>
using namespace std;
struct E
{
int v, t;
} e[200050];
bool b[100050];
vector<pair<int, int>> q[100050];
int n, m, c, R, T, r[100050], d[100050], s[100050], p[100050], h[100050], C[100050], D[100050], G[100050];
void A(int u, int v)
{
e[++c] = {v, h[u]};
h[u] = c;
}
void X(int u, int k, int t)
{
s[u] = 1;
p[u] = 0;
for (int i = h[u], v; i; i = e[i].t)
if (!b[v = e[i].v] && v != k)
X(v, u, t), s[u] += s[v], p[u] = max(p[u], s[v]);
if (p[R] > (p[u] = max(p[u], t - s[u])))
R = u;
}
void Y(int u, int k, bool t)
{
t ? !C[d[u]]++ && (G[++G[0]] = d[u]) : D[++D[0]] = u;
for (int i = h[u], v; i; i = e[i].t)
if (!b[v = e[i].v] && v != k)
d[v] = d[u] + 1, Y(v, u, t);
}
void Q(int u, int k)
{
b[u] = 1;
d[u] = 0;
Y(u, k, 1);
for (auto i : q[u])
r[i.second] += C[i.first];
for (int i = h[u], v; i; i = e[i].t)
if (!b[v = e[i].v] && v != k)
{
d[v] = 1;
Y(v, u, 0);
for (int j = 1; j <= D[0]; ++j)
--C[d[D[j]]];
for (int j = 1; j <= D[0]; ++j)
for (auto k : q[D[j]])
if (k.first >= d[D[j]])
r[k.second] += C[k.first - d[D[j]]];
for (int j = 1; j <= D[0]; ++j)
++C[d[D[j]]];
D[0] = 0;
}
for (int i = 1; i <= G[0]; ++i)
C[G[i]] = 0;
G[0] = 0;
for (int i = h[u], v; i; i = e[i].t)
if (!b[v = e[i].v] && v != k)
p[R = 0] = 1e9, X(v, u, s[v]), X(R, 0, s[v]), Q(R, u);
}
int main()
{
scanf("%d", &T);
while (T--)
{
c = 0;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
b[i] = h[i] = 0, q[i].clear();
for (int i = 1, u, v; i < n; ++i)
scanf("%d%d", &u, &v), A(u, v), A(v, u);
for (int i = 0, x, k; i < m; ++i)
scanf("%d%d", &x, &k), q[x].push_back({k, i}), r[i] = 0;
p[R = 0] = 1e9;
X(1, 0, n);
X(R, 0, n);
Q(R, 0);
for (int i = 0; i < m; ++i)
printf("%d\n", r[i]);
}
return 0;
}