题目描述:
小A这次来到一个景区去旅游,景区里面有N个景点,景点之间有N-1条路径。小A从当前的一个景点移动到下一个景点需要消耗一点的体力值。但是景区里面有两个景点比较特殊,它们之间是可以直接坐观光缆车通过,不需要消耗体力值。而小A不想走太多的路,所以他希望你能够告诉它,从当前的位置出发到他想要去的那个地方,他最少要消耗的体力值是多少。
链接:https://ac.nowcoder.com/acm/problem/23482
来源:牛客网
输入描述:
第一行一个整数N代表景区的个数。
接下来N-1行每行两个整数u,v代表从位置u到v之间有一条路径可以互相到达。
接下来的一行两个整数U,V表示这两个城市之间可以直接坐缆车到达。
接下来一行一个整数Q,表示有Q次询问。
接下来的Q行每行两个整数x,y,代表小A的位置在x,而他想要去的地方是y。
输出描述:
对于每个询问下x,y输出一个结果,代表x到y消耗的最少体力对于每个询问下x,y输出一个结果,代表x到y消耗的最少体力。
思路:看到这题要注意有N-1条路径,这说明这是一颗树,那我们就可以利用lca来求两点之间的最短路,用两点到根的距离减去两倍lca到根的距离。答案需要考虑三种情况,不坐电缆,坐电缆u,做电缆v,分别求出答案再求最小值即可。
代码:
#include <iostream> #include <bits/stdc++.h> using namespace std; vector<int> g[400005]; int dep[400005],f[400005][100],lg[400005]; int dist[400005]; //f[i][j]表示i往上走2^j步到达的点 void dfs(int u,int fa){ dep[u]=dep[fa]+1; f[u][0]=fa; for(int j=1;(1<<j)<=dep[u];j++)//u的2^(j-1)祖先的2^(j-1)祖先 f[u][j]=f[f[u][j-1]][j-1]; for(int i=0;i<(int)g[u].size();i++){ int v=g[u][i]; if(v!=fa){ dist[v]=dist[u]+1; dfs(v,u); } } } void init(int n){ for(int i=1;i<=n;i++)//预处理出log2(i)+1的值,便于直接调用 lg[i]=lg[i-1]+(1<<lg[i-1]==i); } int lca(int u,int v){ if(dep[u]<dep[v])swap(u,v);//不妨设u的深度 >= v的深度,便于下面统一处理 while(dep[u]>dep[v])u=f[u][lg[dep[u]-dep[v]]-1];//跳到统一深度 if(u==v)return u; for(int j=lg[dep[u]]-1;j>=0;j--)//从步数大往小的跳 if(f[u][j]!=f[v][j])//因为我们要跳到它们LCA的下面一层,所以它们肯定不相等,如果不相等就跳过去 u=f[u][j],v=f[v][j]; return f[u][0];//返回父节点 } int main() { int n; scanf("%d",&n); for(int i=0;i<n-1;i++){ int u,v; scanf("%d%d",&u,&v); g[u].push_back(v); g[v].push_back(u); } init(n); dist[1]=0; dfs(1,0); int u,v; scanf("%d%d",&u,&v); int q; scanf("%d",&q); while(q--){ int x,y; scanf("%d%d",&x,&y); int id=lca(x,y); int ans=dist[x]+dist[y]-2*dist[id]; int id1=lca(x,u),id2=lca(y,v); ans=min(ans,dist[x]+dist[u]-2*dist[id1]+dist[y]+dist[v]-2*dist[id2]); id1=lca(x,v),id2=lca(y,u); ans=min(ans,dist[x]+dist[v]-2*dist[id1]+dist[y]+dist[u]-2*dist[id2]); printf("%d\n",ans); } return 0; }