P8805 [蓝桥杯 2022 国 B] 机房 题解

GoodCoder666

2023-01-02 16:11:33

Solution

## 题意 给定一棵 $n$ 个结点的树,每个结点的入度/出度即为其权值。一条路径的长度定义为**经过所有结点的权值总和**(包括起始和终止结点)。 回答 $m$ 个询问,对于 $i=1,2,\dots,m$: - 结点 $u_i$ 和 $v_i$ 之间的最短路径长度是多少? $1\le n,m\le 10^5$。 ## 分析 首先考虑最短路的方法,$\mathcal O(n^3+m)$ 的 Floyd 和 $\mathcal O(nm)$ 的 DFS 肯定不行。 树上的路径问题一般使用 LCA,没学过的可以先看[OI Wiki上的介绍](https://oi-wiki.org/graph/lca/)。本题解中 LCA 使用预处理 $\mathcal O(n\log n)$,查询 $\mathcal O(\log n)$ 的**倍增算法**。 思路很明确: - 预处理出从根结点开始,到每个结点 $u$ 的路径长度 $\mathrm{dis}_u$。根结点任意,但**必须与LCA所使用的根结点相同**。 - 对于一次查询 $u\to v$,令 $f=\mathrm{LCA}(u,v)$。 - 此时,$u\to v$ 的最短路径肯定是 $u\to f\to v$。 - $u\to f$ 的路径长度(不包括 $f$)为 $\mathrm{dis}_u-\mathrm{dis}_f$。 - $v\to f$ 的路径长度(不包括 $f$)为 $\mathrm{dis}_v-\mathrm{dis}_f$。 - 于是,答案为 $\mathrm{dis}_u-\mathrm{dis}_f+\mathrm{dis}_v-\mathrm{dis}_f+\mathrm{cost}(f)=\mathrm{dis}_u+\mathrm{dis}_v-2\mathrm{dis}_f+\mathrm{cost}(f)$。 **注意计算答案时不要忘记加上$f$的权值。** 由于所有权值的和为 $2(N-1)$,肯定不会爆 `int`,所以直接使用 `int` 即可。 ## 代码 [提交记录传送门](https://www.luogu.com.cn/record/98500834) 跑得挺快的,没开 `O2`,$10$ 个点总共 $807\text{ms}$。 ```cpp #include <cstdio> #include <vector> #define maxn 100005 using namespace std; vector<int> G[maxn]; int depth[maxn], fa[maxn][17]; // 2^17=131072 int dis[maxn]; #define cost(v) (int)G[v].size() void dfs(int v, int par) { int d = depth[v] + 1; fa[v][0] = par; for(int i=1; (1<<i)<d; i++) fa[v][i] = fa[fa[v][i - 1]][i - 1]; dis[v] += cost(v); for(int u: G[v]) if(u != par) { dis[u] = dis[v]; depth[u] = d; dfs(u, v); } } int lca(int u, int v) { if(depth[u] < depth[v]) u ^= v ^= u ^= v; int m = depth[u] - depth[v]; for(int i=0; m; i++, m>>=1) if(m & 1) u = fa[u][i]; if(u == v) return u; for(int i=__builtin_log2(depth[u]); i>=0; i--) if(fa[u][i] != fa[v][i]) u = fa[u][i], v = fa[v][i]; return fa[u][0]; } int main() { int n, q; scanf("%d%d", &n, &q); for(int i=1; i<n; i++) { int x, y; scanf("%d%d", &x, &y); G[--x].push_back(--y); G[y].push_back(x); } dfs(0, -1); while(q--) { int u, v; scanf("%d%d", &u, &v); int f = lca(--u, --v); printf("%d\n", dis[u] + dis[v] - 2 * dis[f] + cost(f)); } return 0; } ```