做到这里,我们已经把子树内的贡献解决了。然后发现子树外的不是很好做。如果定义 g[u][i] 为以 u 为根的子树外,与 u 距离为 i 的点的个数,时空复杂度完全无法承受。我为此困惑了很久(试图长剖换根 DP)。回顾 f 的过程,发现状态数由链长限制,这启发我们把 g[u][i] 限制到长链以内。即重新定义 g[u][i] 为 以 u所在长链的链顶为根的子树内,以 u 为根的子树外,与 u 距离为 i 的点的个数。此时状态数被控制在了二倍链长以内(从长链的底端的叶子到另一个叶子,而另一个叶子的深度不大于长链的底端的叶子)。
明确了 g 之后,转移不难。如果自己是长儿子,就继承父亲。然后再暴力合并上短儿子的 f。这里是 f 哦,看清!
// 第一遍dfs 长剖 voiddfs1(int u, int fa){ for (int i = h[u]; i; i = e[i].ne) { int v = e[i].to; if (v == fa) continue; dfs1(v, u); if (u[son][hei] < v[hei]) { u[son] = v; } } u[far] = fa; u[hei] = u[son][hei] + 1; }
inlineint &f(int i, int j){ if (j >= hei[i] || j < 0) return zero; // 询问里的 k 可能超过当前链长 elsereturn fff[i[dfn] + j]; }
// 第二遍 dfs 求 f voiddfs2(int u, int tp){ u[top] = tp; u[dfn] = ++ dnt; if (u[son]) { dfs2(u[son], tp); } for (int i = h[u]; i; i = e[i].ne) { int v = e[i].to; if (v == u[far] || v == u[son]) { continue; } dfs2(v, v); } /* 注意,这里并没有合并短儿子 f 仅包含长儿子内的信息 */ for (auto i : Q[u]) { int x = u; int y = i.second; while (x && y >= 0) { ans[i.first] += f(x, y); // 跳长链 y -= x[top][hei] - x[hei] + 1; x = x[top][far]; } } // 合并信息 f(u, 0) = 1; for (int i = h[u]; i; i = e[i].ne) { int v = e[i].to; if (v == u[far] || v == u[son]) { continue; } for (int j = 0; j < v[hei]; ++ j) { f(u, j + 1) += f(v, j); } } }
inlineint &g(int i, int j){ if (i[top][hei] + i[top][hei] - i[hei] <= j) return zero; return ggg[i[dfn] + i[top][dfn] + i[top][hei] - j - 1]; }
// 第三遍 dfs 求 g voiddfs3(int u){ /* 此时 g 为 u 子树以外的信息 */ g(u, 0) = 1; for (int i = h[u]; i; i = e[i].ne) { int v = e[i].to; if (v == u[far] || v == u[son]) continue; for (int j = 0; j < v[hei]; ++ j) { g(u, j + 1) += f(v, j); /* 因为短儿子的 DP 数组不会被继承 所以 f(v, j) 是可用的 */ } } /* 此时 g 为长儿子以外的信息 */ for (auto i : Q[u]) { // 本长链内,长儿子外的信息 ans[i.first] += g(u, i.second); int x = u[top]; int y = i.second - x[hei] + u[hei]; while (x > 1 && y > 0) { /* 注意这里脑抽的跳长链方式 因为上一层里统计了这一层的信息(相当于走出长链又立刻走回来了 所以我们要减去一个 f 由于我们要同时访问两条链的信息,所以只能停在长链顶 */ ans[i.first] += g(x[far], y - 1) - f(x, y - 2); x = x[far]; y -= x[top][hei] - x[hei] + 1; x = x[top]; } } /* 此时 g 为长儿子以外的信息,它还要处理短儿子内的询问 */ for (int i = h[u]; i; i = e[i].ne) { int v = e[i].to; if (v == u[far] || v == u[son]) continue; dfs3(v); } /* g[u] 使命结束,传给 g[son[u]] */ if (u[son]) { dfs3(u[son]); } }
inlinevoidINIT(){ }
inlinevoidWORK(){ rd(n, m); for (int i = 2; i <= n; ++ i) { int x, y; rd(x, y); add(x, y); add(y, x); } for (int i = 1; i <= m; ++ i) { int x, k; rd(x, k); Q[x].push_back({i, k}); ans[i] = 0; } dfs1(1, 0); dfs2(1, 1); dfs3(1); for (int i = 1; i <= m; ++ i) { wr(ans[i], '\n'); ans[i] = 0; } idx = 0; dnt = 0; for (int i = 1; i <= n; ++ i) { h[i] = 0; hei[i] = 0; son[i] = 0; fff[i] = 0; ggg[i * 2] = 0; ggg[i * 2 + 1] = 0; Q[i].clear(); } }