P8805 [蓝桥杯 2022] 机房

题意

给出一颗大小为 nn 的树和 mm 次询问,每个点的权值为它的边数。每次询问输入两个正整数 u,vu,v,输出 uuvv 的路径大小。

数据范围:n,m105n,m\leq 10^5

思路

题目虽然说是求 uuvv 的最短路径,但对于一颗树来说,只需要保证不重复经过边即可保证这点。也就是说只要能把点权转化一下,这道题就变成了 LCALCA 的模板题了。(不会 LCALCA这里有模板

将点权作为这个点与它的父亲的边权,用 LCALCA 算出路径的长度之后,会发现 LCALCA处的点权被抵消了,再加上 LCALCA 处的点权即为答案。

举一个例子来说明一下:

先把点权转化为边权,计算出所有点到根节点 11 的距离 disdis。对应代码dis[v]=dis[u]+a[v];

若要计算节点 6677 的距离,可以发现它们路径上深度最小的点便是它们的最近公共祖先。节点 6677 到根节点 11 的路径合并后,多余的地方便是它们的 LCALCA 节点 33 到根节点的距离乘二。此时节点 33 的点权没被计算到,加上就行了。

其他细节见代码。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include<bits/stdc++.h>
using namespace std;

const int N=1e5+9;
int n,m,a[N],fa[N][21],dep[N],dis[N];
vector<int> e[N];

void dfs(int u,int f){
fa[u][0]=f;
dep[u]=dep[f]+1;
for(int i=1;i<=20;i++) fa[u][i]=fa[fa[u][i-1]][i-1];
for(int v:e[u]){
if(v==f) continue;
dis[v]=dis[u]+a[v];//a[v]即为u,v边权,dis[v]为v与根节点的距离
dfs(v,u);
}
}
int lca(int u,int v){//倍增求lca
if(dep[u]<dep[v]) swap(u,v);
for(int i=20;i>=0;i--){
if(dep[fa[u][i]]>=dep[v]) u=fa[u][i];
}
if(u==v) return u;
for(int i=20;i>=0;i--){
if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
}
return fa[u][0];
}

int main(){
scanf("%d%d",&n,&m);
int u,v;
for(int i=1;i<n;i++){
scanf("%d%d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
a[u]++,a[v]++;//点权
}
dfs(1,0);
while(m--){
scanf("%d%d",&u,&v);
int d=lca(u,v);
printf("%d\n",dis[u]+dis[v]-2*dis[d]+a[d]);//路径长度+lca处点权
}
return 0;
}
打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2022 GMXH
  • 访问人数: | 浏览次数:

我很可爱,请给我钱~

支付宝
微信