小红的树上路径查询(hard)
小红的树上路径查询(hard)
题目描述
本题和 难度的区别是,询问的次数有多次!
小红拿到了一棵树,她有多次询问,每次询问输入一条简单路径 ,她想知道树上所有节点到该路径的最短路之和是多少,你能帮帮她吗?
定义节点到路径的最短路为:节点到路径上所有点的最短路中,值最小的那个。特殊的,如果节点在路径上,则最短路为 。
简单路径:从树上的一个节点出发,沿着树的边走,不重复地经过树上的节点,到达另一个节点的路径。
输入描述:
第一行输入两个正整数 ,代表节点数量和询问次数。
接下来的 行,每行输入两个正整数 ,代表节点 和节点 有一条边连接。
接下来的 行,每行输入两个正整数 ,代表一次询问。
输出描述:
输出 行,每行输出一个整数,代表询问的答案。
示例1
输入
4 2
1 2
1 3
1 4
2 3
2 1
输出
1
2
解题思路
不会,直接参考的题解。
对于 与 构成的 路径外一点 ,假设 到 路径上最近的点是 ,那么 到 或 到 的路径必定包含点 (假设以 为根进行 bfs,那么第一次遍历到 路径上的点就是 ,继而从 扩展到 路径上的其他点)。这点其实还是很难想到的。
然后求 分别到 和 距离之和。有
即有 。这条式子有什么用呢?实际上我们关心的是所有点到 (即该点到 路径最近的点)的距离的和,并不需要求出具体的 。同时可以发现如果 也是 路径上的点上式同样成立。因此所有点关于 的和就是
其中 和 分别是 和 到所有点的距离总和,这个可以用换根 dp 求得。 可以分别求出 和 到最近公共祖先的距离再求和。
下面简单讲一下如何求所有点到 的距离总和。固定 为根,定义 表示子树 中所有点到 的距离总和, 表示从 往上走的所有点(其实就是除了子树 外的点)到 的距离总和。那么状态转移方程就是
其中 表示子树 的大小, 表示 的父节点。那么所有点到 的距离总和就是 。
AC 代码如下,时间复杂度为 :
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 5, M = N * 2;
int n, m;
int h[N], e[M], ne[M], idx;
LL sz[N], f[N], g[N];
int fa[N][17], d[N];
void add(int u, int v) {
e[idx] = v, ne[idx] = h[u], h[u] = idx++;
}
void dfs1(int u, int p) {
sz[u] = 1;
d[u] = d[p] + 1;
fa[u][0] = p;
for (int i = 1; i <= 16; i++) {
fa[u][i] = fa[fa[u][i - 1]][i - 1];
}
for (int i = h[u]; i != -1; i = ne[i]) {
int v = e[i];
if (v == p) continue;
dfs1(v, u);
sz[u] += sz[v];
f[u] += f[v] + sz[v];
}
}
void dfs2(int u, int p) {
for (int i = h[u]; i != -1; i = ne[i]) {
int v = e[i];
if (v == p) continue;
g[v] = g[u] + f[u] - (f[v] + sz[v]) + n - sz[v];
dfs2(v, u);
}
}
int lca(int a, int b) {
if (d[a] < d[b]) swap(a, b);
for (int i = 16; i >= 0; i--) {
if (d[fa[a][i]] >= d[b]) a = fa[a][i];
}
if (a == b) return a;
for (int i = 16; i >= 0; i--) {
if (fa[a][i] != fa[b][i]) a = fa[a][i], b = fa[b][i];
}
return fa[a][0];
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
memset(h, -1, sizeof(h));
for (int i = 0; i < n - 1; i++) {
int u, v;
cin >> u >> v;
add(u, v), add(v, u);
}
dfs1(1, 0);
dfs2(1, 0);
while (m--) {
int x, y;
cin >> x >> y;
cout << (f[x] + g[x] + f[y] + g[y] - n * (d[x] + d[y] - 2 * d[lca(x, y)])) / 2 << '\n';
}
return 0;
}
参考资料
牛客周赛64题解:https://ac.nowcoder.com/discuss/1421788
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/18498311
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2023-10-23 D2. Dances (Hard Version)
2023-10-23 B. Raspberries
2023-10-23 C. Medium Design
2022-10-23 A. Bestie
2022-10-23 三元组