题目大意:有N个点,给定M个集合,集合Si里面的点两两之间的距离都为Ti,集合里面的所有点数之和<=1e6。两个人分别从1和n出发,要求相遇的最短距离,并输出相遇的点(可能多个)。
解题思路:首先无疑是最短路,然后因为同一个点可能属于两个或多个集合,故需要虚电。除了n个点外,每一个集合建一个新的点与集合中的点相连,集合中的点要到集合中的另一个点要先经过新建的点,所以走的路变成了2倍,分别从1和n各走一遍最短路,最后答案除以2即可。

又见最短路 + 虚电 https://blog.csdn.net/qq_40831340/article/details/99672543

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> P;
int cas, n, m;

int head[maxn], cnt;
int to[maxn << 1], nxt[maxn << 1], val[maxn << 1];

void ade(int a, int b, int c) {
	to[++ cnt] = b, val[cnt] = c;
	nxt[cnt] = head[a], head[a] = cnt;
}

bool vis[maxn];
int dis[maxn], d[maxn];
void dj(int s) {
	priority_queue<P> que;
	que.push(P(0, s));
	memset(dis, 0x3f, sizeof dis);
	memset(vis, 0, sizeof vis);
	dis[s] = 0;
	while(!que.empty()) {
		P p = que.top();
		que.pop();
		int u = p.second;
		if(vis[u]) continue;
		vis[u] = 1;
		for(int i = head[u]; i; i = nxt[i]) {
			int v = to[i];
			if(dis[v] > dis[u] + val[i]) {
				dis[v] = dis[u] + val[i];
				que.push(P(-dis[v], v));
			}
		}
	}
}
vector<int> ans;
int main() {
	scanf("%d", &cas);
	for(int tt = 1; tt <= cas; tt ++) {
		scanf("%d %d", &n, &m);
		memset(head, 0, sizeof head);
		cnt = 0;
		for(int i = 1, u, val, t; i <= m; i ++) {
			scanf("%d %d", &val, &t);
			for(int j = 1; j <= t; j ++) {
				scanf("%d", &u);
				ade(u, n + i, val), ade(n + i, u, 0);
			}
		}
		printf("Case #%d: ", tt);
		dj(1);
		if(dis[n] == INF) puts("Evil John");
		else {
			for(int i = 1; i <= n; i ++) d[i] = dis[i];
			dj(n);
			ans.clear();
			int tmp = INF;
			for(int i = 1; i <= n; i ++) {
				int mint = max(d[i], dis[i]);
				if(mint < tmp) {
					tmp = mint;
					ans.clear();
					ans.push_back(i);
				} else if(mint == tmp) ans.push_back(i);
			}
			printf("%d\n", tmp);
			for(int i = 0; i < ans.size(); i ++) {
				if(i != 0) printf(" ");
				printf("%d",ans[i]);
			}
			puts("");
		}
	}
	return 0;
}