You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

141 lines
5.5 KiB

2 years ago
#include <bits/stdc++.h>
using namespace std;
const int N = 25010, M = 150010;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> PII;
// 存图
int idx, h[N], e[M], w[M], ne[M];
void add(int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
int T; // 城镇数量
int R; // 道路数量
int P; // 航线数量
int S; // 出发点
// 下面两个数组是一对
int id[N]; // 节点在哪个连通块中
vector<int> block[N]; // 连通块包含哪些节点
int bcnt; // 连通块序号计数器
int dist[N]; // 最短距离(结果数组)
int in[N]; // 每个DAG(节点即连通块)的入度
bool st[N]; // dijkstra用的是不是在队列中的数组
queue<int> q; // 拓扑序用的队列
// 将u节点加入团中,团的番号是 bid
void dfs(int u, int bid) {
id[u] = bid; // ① u节点属于bid团
block[bid].push_back(u); // ② 记录bid团包含u节点
// 枚举u节点的每一条出边将对端的城镇也加入到bid这个团中
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
if (!id[v]) dfs(v, bid); // Flood Fill
}
}
// 计算得到bid这个连通块中最短距离
void dijkstra(int bid) {
priority_queue<PII, vector<PII>, greater<PII>> pq;
/*
distinf,
dijkstra
din[]
*/
for (auto u : block[bid]) pq.push({dist[u], u});
while (pq.size()) {
int u = pq.top().second;
pq.pop();
if (st[u]) continue;
st[u] = true;
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
if (st[v]) continue;
if (dist[v] > dist[u] + w[i]) {
dist[v] = dist[u] + w[i];
// 如果是同团中的道路,需要再次进入Dijkstra的小顶堆以便计算完整个团中的路径最小值
if (id[u] == id[v]) pq.push({dist[v], v});
}
/*如果u和v不在同一个团中,说明遍历到的是航线
id[v]:v
--in[id[v]] == 0: u->vid[v]id[v]
queue
*/
if (id[u] != id[v] && --in[id[v]] == 0) q.push(id[v]);
}
}
}
// 拓扑序
void topsort() {
for (int i = 1; i <= bcnt; i++) // 枚举每个团
if (!in[i]) q.push(i); // 找到所有入度为0的团,DAG的起点
// 拓扑排序
while (q.size()) {
int bid = q.front(); // 团番号
q.pop();
// 在此团内部跑一遍dijkstra
dijkstra(bid);
}
}
int main() {
memset(h, -1, sizeof h); // 初始化
scanf("%d %d %d %d", &T, &R, &P, &S); // 城镇数量,道路数量,航线数量,出发点
memset(dist, 0x3f, sizeof dist); // 初始化最短距离
dist[S] = 0; // 出发点距离自己的长度是0,其它的最短距离目前是INF
int a, b, c; // 起点,终点,权值
while (R--) { // 读入道路
scanf("%d %d %d", &a, &b, &c);
add(a, b, c), add(b, a, c); // 连通块内是无向图
}
/* 航线本质是 团与团 之间单向连接边
DAG
DAGbid;
:
(1) id[i]:i()
(2) vector<int> block[N] :
Q:
A:线
dfsFlood Fill,
线线
*/
// 缩点
for (int i = 1; i <= T; i++) // 枚举每个小节点
if (!id[i]) // 如果它还没有标识是哪个团,就开始研究它,把它标识上隶属于哪个团,并且,把和它相连接的其它点也加入同一个团中
dfs(i, ++bcnt); // 需要提前申请好番号bcnt
// 航线
while (P--) {
scanf("%d %d %d", &a, &b, &c);
add(a, b, c); // 单向边
in[id[b]]++; // b节点所在团入度+1
}
// 拓扑序
topsort();
// 从S到达城镇i的最小花费
for (int i = 1; i <= T; i++) {
if (dist[i] > INF / 2)
puts("NO PATH");
else
cout << dist[i] << endl;
}
return 0;
}