题目大意:给定一张\(N\)个点的图,构成了\(M\)个团,每一个团内的边权均相等,求图上有多少个点知足到\(1\)号节点和\(N\)号节点的最大值最小。node
题解:
本题的核心是如何优化连边,考虑对于每个团增长一个虚拟节点,并让每一个节点到虚拟节点连一条边权为\(t_i\)的有向边,虚拟节点到团中每个点连一条边权为\(0\)的有向边,最后跑最短路求解便可。ios
注意:数据中有\(0\)下标的节点,读入时须要忽略,别问我怎么知道的。。。c++
代码以下优化
#include <bits/stdc++.h> using namespace std; typedef long long LL; struct node { int to; LL w; node(int _to = 0, LL _w = 0) { to = _to; w = _w; } }; int main() { ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); int T, kase = 0; cin >> T; while (T--) { int n, m; cin >> n >> m; vector<vector<node>> adj(n + m); for (int i = 0; i < m; i++) { LL w; int cnt; cin >> w >> cnt; while (cnt--) { int x; cin >> x; if (x == 0) continue; x--; adj[i + n].emplace_back(x, 0); adj[x].emplace_back(i + n, w); } } auto dij = [&](int st) { vector<int> expand(n + m, 0); vector<LL> d(n + m, 1e18); priority_queue<pair<LL, int>> q; d[st] = 0, q.push(make_pair(0, st)); while (!q.empty()) { int x = q.top().second; q.pop(); if (expand[x]) continue; expand[x] = 1; for (auto e : adj[x]) { int y = e.to, w = e.w; if (d[y] > d[x] + w) { d[y] = d[x] + w; q.push(make_pair(-d[y], y)); } } } return d; }; vector<LL> dst = dij(0); vector<LL> ded = dij(n - 1); if (dst[n - 1] == 1e18) { cout << "Case #" << ++kase << ": Evil John" << endl; } else { LL ans = 1e17; vector<int> p; for (int i = 0; i < n; i++) { LL ret = max(dst[i], ded[i]); if (ret < ans) { ans = ret; p.clear(); p.push_back(i + 1); } else if (ret == ans) { p.push_back(i + 1); } } cout << "Case #" << ++kase << ": " << ans << endl; cout << p[0]; for (int i = 1; i < (int)p.size(); i++) { cout << " " << p[i]; } cout << endl; } } return 0; }