2026“钉耙编程”中国大学生算法设计春季联赛(8)1001思路分享(bfs)

题意概述

给定一张 \(n\) 个节点,\(m\) 条边的简单连通无向图。初始 \(A\)\(1\)\(B\)\(n\),她们同时出发,\(A\) 会选择任意一条到 \(n\) 最短路走到 \(n\),然后选择任意一条到 \(1\) 的最短路走回 \(1\)\(B\) 想要在不与 \(A\) 相遇的情况下走到 \(1\),并且比 \(A\) 早到。

如果 \(B\) 能实现她的目标,输出 \(Kasumi\) 和最短的时间,否则输出 \(Arisa\)

多测,\(\sum{n} \le 10^6\)\(\sum{m} \le 2\times 10^6\)

思路

首先求从 \(1\) 出发的单源最短路,用 \(vis\) 标记所有最短路上的节点,记为 \(dis\)

然后求从 \(n\) 出发,严格不与 \(A\) 相遇的多源最短路,记为 \(dis2\)(实际可能存在方案能不与 \(A\) 相遇)。

具体做法是,考虑 \(u\)->\(v\) 的松弛,如果 \(vis[v]=0\),可以直接走;否则需要从 \(n\) 出发到达 \(v\) 的时间严格小于从 \(1\) 出发到达 \(v\) 的时间,也就是 \(dis2_u + 1 \lt dis_v\)。以上两种情况是绝对不会相遇的。

考虑 \(B\) 的路径形式,一定是先走到某些 \(vis[u]=0\) 的节点来避免与 \(A\) 相遇,等 \(A\) 走了之后,再走到 \(vis[u]=1\) 的节点去往 \(1\)

于是可以只考察 \(vis_u=0\) 并且 \(vis_v=1\) 的边 \(u\)->\(v\)

如果 \(dis2_u \lt dis_v\),即 \(B\) 到达 \(u\) 的时间比 \(A\) 到达 \(v\) 的时间早,需要等到 \(dis_v\),然后往 \(1\) 走;如果 \(dis2_u \ge dis_v\)\(A\) 已经离开 \(v\) 了,直接走即可。

用时为 \(\max(dis2_u,dis_v)+1+dis_v\)

时间复杂度 \(\mathcal{O}(n+m)\)

代码

//author:kzssCCC

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

const ll INF = 9e18;

void solve(){
	int n,m;
	cin >> n >> m;

	vector<vector<int>> adj(n+1);
	for (int i=0;i<m;i++){
		int u,v;
		cin >> u >> v;
		adj[u].push_back(v);
		adj[v].push_back(u);
	}

	vector<ll> dis(n+1,INF);
	queue<int> q;
	dis[1] = 0;
	q.push(1);

	while (!q.empty()){
		int u = q.front();
		q.pop();

		for (auto& v:adj[u]){
			if (dis[u]+1<dis[v]){
				dis[v] = dis[u]+1;
				q.push(v);
			}
		}
	} 

	vector<bool> vis(n+1,false);
	vis[n] = true;
	q.push(n);
	
	while (!q.empty()){
		int u = q.front();
		q.pop();

		for (auto& v:adj[u]){
			if (dis[v]==dis[u]-1){
				vis[v] = true;
				q.push(v);
			}
		}
	}	

	vector<ll> dis2(n+1,INF);
	dis2[n] = 0;
	q.push(n);

	while (!q.empty()){
		int u = q.front();
		q.pop();

		for (auto& v:adj[u]){
			if (dis2[v]<=dis2[u]+1) continue;
			if (!vis[v] || dis[v]>dis2[u]+1){
				dis2[v] = dis2[u]+1;
				q.push(v);
			}
		}
	}

	ll res = INF;
	for (int u=1;u<=n;u++){
		if (vis[u]) continue;
		for (auto& v:adj[u]){
			if (!vis[v]) continue;

			res = min(res,max(dis2[u],dis[v])+1+dis[v]);
		}
	}

	if (res<2*dis[n]){
		cout << "Kasumi" << ' ' << res << '\n';
	}
	else{
		cout << "Arisa" << '\n';
	}
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	int t;
	cin >> t;
	while (t--) solve();

	return 0;
}
posted @ 2026-05-18 12:58  kzssCCC  阅读(14)  评论(0)    收藏  举报