题目描述:
农夫约翰想把牛奶送到T个城镇,城镇之间通过 R 条道路和P条航线连接。
城镇Ai和Bi通过一条道路或航线连接,道路或航线需要花费Ci,道路的花费为正数,航线的花费可能是负数,道路是双向的,航线的单向的。
求从城镇S把奶牛送到每个城镇的最便宜的方案。
数据范围:
1<=T<=25000,1<=R , P<=50000
1<=Ai , Bi , Ci , S<=T
输出:
城镇S到城镇i的的最小花费,如果不连通则输出NO PATH
solution:
我们可以将用道路连接的城镇看成一个团,而由于航线是单向的,那我们便可以利用拓扑排序依此遍历每个团以求出S点到每个点的最短路。
对于团,我们可以用并查集来维护团里的点,然后用一个block向量来储存一个连通块的中每个点。
对于拓扑排序的队列更新,我们可以取出队头,每次将队头点的团中的所有点取出后,做一遍dijkstra,同时在更新最短路的同时判断,被更新的点是团内还是团外,如果是团内则存入dijkstra的堆中,如果是团外则存入拓扑排序的队列之中。
C++Code:
#include <bits/stdc++.h>
#define lowbit(x) (x & -x)
using namespace std;
typedef long long ll;
typedef pair<int, int>PII;
const int N = 3e5 + 10, M = 2e5 + 10, INF = 0x3f3f3f3f;
int n, mr, mp, s;
int a[N];
int h[N], e[M], w[M], ne[M], idx = 0;
int dist[N];
int p[N], id[N];
bool st[N];
vector<int>block[N];
queue<int>q;
void add(int a, int b, int c) {
w[idx] = c, e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int find(int x) {
if (x != p[x])p[x] = find(p[x]);
return p[x];
}
void dijkstra(int bid) {//利用堆优化dijkstra更新最短路的同时找出入度减为0的连通块并更新拓扑排序的队列
priority_queue<PII, vector<PII>, greater<PII>>heap;
for (auto u : block[bid]) heap.push({ dist[u],u });
while (!heap.empty()) {
auto t = heap.top();
heap.pop();
int ver = t.second, distance = t.first;
if (st[ver])continue;
st[ver] = true;
for (int i = h[ver]; i != -1; i = ne[i]) {
int j = e[i];
int x = find(j), y = find(ver);
if (dist[j] > w[i] + distance) {
dist[j] = w[i] + distance;
if (x == y)heap.push({ dist[j],j });
}
if (x != y) {
if (--id[x] == 0)q.push(x);
}
}
}
}
void topsort() {//拓扑排序每个连通块之间的顺序
memset(dist, 0x3f, sizeof dist);
dist[s] = 0;
for (int i = 1; i <= n; i++) {
if (!id[i]) {
q.push(i);
}
}
while (!q.empty()) {
auto t = q.front();
q.pop();
dijkstra(t);
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
memset(h, -1, sizeof h);
cin >> n >> mr >> mp >> s;
for (int i = 1; i <= n; i++)p[i] = i;
for (int i = 1; i <= mr; i++) {
int a, b, c;
cin >> a >> b >> c;
add(a, b, c);
add(b, a, c);
a = find(a), b = find(b);
if (a != b)p[a] = b;
}
for (int i = 1; i <= n; i++) {//将每个数都存入一个连通块中
block[find(i)].push_back(i);
}
for (int i = 1; i <= mp; i++) {
int a, b, c;
cin >> a >> b >> c;
add(a, b, c);
id[find(b)]++;//存入每个连通块的输入
}
topsort();
for (int i = 1; i <= n; i++) {
if (dist[i] > INF / 2)cout << "NO PATH" << endl;
else cout << dist[i] << endl;
}
return 0;
}