P10289 [GESP样题 八级] 小杨的旅游 题解 Solution

题意简述:

无。

题解:

此处阅读体验更佳?

看完题目之后,我们首先要考虑一个问题:什么时候要用到传送门?

对于树上的两个点 x x x y y y,如果不考虑传送门的话,最短距离就是这两个点到它们的最近公共祖先的距离和。

那么如果用传送门更优,那么肯定满足这一个条件: x x x 到它最近的传送门距离加上 y y y 到它最近的传送门距离小于 x x x y y y 到它们的最近公共祖先的距离和。

大家可以自己验证一些普通和特殊情况,此处就不给出证明了。

那么我们就可以先求出每个点到最近的传送门距离 o p i op_i opi,对于询问,再输出 min ⁡ ( o p x + o p y , d e p x − d e p l c a ( x , y ) + d e p y − d e p l c a ( x , y ) ) \min(op_x+op_y,dep_x-dep_{lca(x,y)}+dep_y-dep_{lca(x,y)}) min(opx+opy,depxdeplca(x,y)+depydeplca(x,y))

那么就可以写代码了。

注意:

  • 可能没有传送门(代码中注释 A)。

代码如下:

#include<bits/stdc++.h>
#define N 200100
#define I_love_Furina return//发电+放抄袭(?)
#define forever 0
#define foreverr 1 
#define int long long
using namespace std;
int n,T,m,q,head[N],op[N],num,f[N][20],dep[N];
struct node{int to,nxt;}a[N*2];
inline void add(int u,int v){a[++num].to=v,a[num].nxt=head[u],head[u]=num;}
inline void solve(){//求最近传送阵距离
	queue<int> q;
	for(int i=1,x;i<=m;i++)cin>>x,q.push(x),op[x]=0;//把所有的传送阵位置压入队列,跑bfs
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for(int i=head[u];i;i=a[i].nxt){
			int v=a[i].to;
			if(op[v]!=-1)continue;//避免重复遍历
			op[v]=op[u]+1,q.push(v);
		}
	}
	I_love_Furina ;
}
void dfs(int x,int fa){
	f[x][0]=fa,dep[x]=dep[fa]+1;
	for(int i=1;i<=19&&f[f[x][i-1]][i-1];i++)f[x][i]=f[f[x][i-1]][i-1];//计算祖宗
	for(int i=head[x];i;i=a[i].nxt)if(a[i].to!=fa)dfs(a[i].to,x);
	I_love_Furina ;
} 
inline int lca(int x,int y){//求最近公共祖先
	if(dep[x]<dep[y])swap(x,y);
	for(int i=19;i>=0;i--)if(dep[f[x][i]]>=dep[y])x=f[x][i];
	if(x==y)I_love_Furina x;
	for(int i=19;i>=0;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
	I_love_Furina f[x][0];//注意,是返回x的父亲
}
signed main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>n>>m>>q;
	for(int i=1,u,v;i<n;i++)cin>>u>>v,add(u,v),add(v,u),op[i]=op[i+1]=-1;
	dfs(1,0),solve();//跑lca和最近传送阵距离
	while(q--){
		int x,y;
		cin>>x>>y;
		int LCA=lca(x,y);
		//cout<<lca(x,y)<<" "<<op[x]<<" "<<op[y]<<endl;
		cout<<min(dep[x]-dep[LCA]+dep[y]-dep[LCA],(op[x]==-1||op[y]==-1?3156781267:op[x]+op[y]))<<endl;//A:防止输出-2
	}
	I_love_Furina forever;
}

完结撒花咯(点个赞再走)。

  • 17
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值