題目大意:給定一張\(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; }