牛客月赛13-小A的最短路-(lca)

F

题意:
就是给你n个点,然后n-1条边边权都为1,然后还有一条边但是这条边的边权是0。问你m次两个点的距离最小是多少。

思考:
刚开始我看了看,跑m次最短路?应该会超时的,但是我没别的想法了,看了眼题解。原来是先求lca,那么a和b的最短距离就dep[a]+dep[b]-2*dep[lca(a,b)]。但是还有一个边权是0的边怎么办呢,因为只有这一条是为0的那么完全可以从边权为0的一个端点跑一次最短路,那么a和b的距离就是dist[a]+dist[b]。那么整个复杂度就降下来了。其实好久不用lca对于lca求两点间的最短距离这个想法我已经淡忘了,其实这题也提示你了,为什么给你n-1条边,目的就是提示你这是一个树,而不是图。当然对于0的那条边,还可以这样处理,既然这条边是0,那么不妨求一下lca(a,A)+lca(b,B)或者lca(a,B),lca(b,A)。也就是如果这条边在a和b的最短路径上面那么这样可以求的最小值,如果不在那么就无关。

代码:

int T,n,m,k;
int A,B;
int va[N];
int dist[N],vis[N];

int acc[N][25],dep[N],cnt=22;

vector<PII > e[N];

void dfs(int now,int p)
{
	acc[now][0] = p;
	dep[now] = dep[p]+1;
	for(auto t:e[now])
	{
		int spot = t.fi;
		if(spot==p) continue;
		dfs(spot,now);
	}
}

int lca(int a,int b)
{
	if(dep[a]<dep[b]) swap(a,b);
	for(int i=cnt;i>=0;i--)
	{
		if(dep[acc[a][i]]>=dep[b]) a = acc[a][i];
	}
	if(a==b) return a;
	for(int i=cnt;i>=0;i--)
	{
		if(acc[a][i]!=acc[b][i])
		{
			a = acc[a][i];
			b = acc[b][i];
		}
	}
	return acc[a][0];
}

void spfa()
{
	for(int i=1;i<=n;i++) dist[i] = inf;
	queue<int > q;
	q.push(A);
	dist[A] = 0;
	vis[A] = 1;
	while(q.size())
	{
		auto now = q.front();
		q.pop();
		vis[now] = 0;
		for(auto t:e[now])
		{
			int spot = t.fi,w = t.se;
			if(dist[spot]>dist[now]+w)
			{
				dist[spot] = dist[now]+w;
				if(!vis[spot])
				{
					vis[spot] = 1;
					q.push(spot);
				}
			}
		}
	}
}

signed main()
{
	IOS;
	cin>>n;
	for(int i=1;i<n;i++)
	{
		int a,b;
		cin>>a>>b;
		e[a].pb({b,1});
		e[b].pb({a,1});
	}
	dfs(1,0);
	for(int i=1;i<=cnt;i++)
	{
		for(int j=1;j<=n;j++)
		acc[j][i] = acc[acc[j][i-1]][i-1];
	}
	cin>>A>>B;
	e[A].pb({B,0});
	e[B].pb({A,0});
	spfa();
	cin>>m;
	while(m--)
	{
		int a,b;
		cin>>a>>b;
		cout<<min(dep[a]+dep[b]-2*dep[lca(a,b)],dist[a]+dist[b])<<"\n";
	}
	return 0;
	/* 直接判断A和B这条边是否在a和b的最短里就行
	cin>>A>>B;
	cin>>m;
	while(m--)
	{
		int a,b;
		cin>>a>>b;
		cout<<min(get(a,b),min(get(a,A)+get(b,B),get(a,B)+get(b,A)))<<"\n";
	}
	return 0;
	*/
}

总结:
多想想以前的算法,把各种知识结合起来。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值