思路
题中给出的图有单向边和双向边,其中单向边边权可能为负,这其实就说明我们不能直接用dijkstra。(貌似可以双端队列优化的spfa水过去= =
注意到无向边边权是非负的,这提示我们可以在无向边上跑最短路。并且我们可以知道,如果将无向边连接的点缩为一点,最后图中只剩下有向边的话,这个图就是一个DAG,DAG上更新最短路还是很容易的。
所以我们就可以分开考虑,首先求出若干个由无向边组成的连通块,对于块内的点,通过堆优化的dijkstra算法更新最短路;然后在块与块之间,类似于拓扑排序,一层一层地进行更新,最后就可以求出源点\(s\)到所有点的最短路了。
但这里有个细节就是,对于不能到达的点,要删去它们相应的出边,否则拓扑排序就不能正常执行(脑补一下就知道了)。这也就是为什么一开始加入\(s\)所在块时,还要把其它入度为0的块也算进去的原因。(我一开始就是这里卡了好久
还有就是代码中最后判不可到达时,因为题目中路径长度最多为\(25000*10000=2.5*10^8\)。那么能到达的点的最短路值一定小于\(2.5*10^8\)。一开始我们将\(d\)数组置为无穷大(也就是\(2e9\)左右),\(0x3f3f3f3f\)大小为\(1e9\),如果最后的最短路长度大于\(0x3f3f3f3f\),那么一定是不可到的。
详见代码
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int N = 50005, M = 100005;
int n, r, p, s;
struct Edge{
int v, next, w;
}e[M << 1];
struct node{
int d, u;
bool operator < (const node &A) const {
return d > A.d;
}
};
int head[N], tot;
void adde(int u, int v, int w) {
e[tot].v = v; e[tot].w = w; e[tot].next = head[u]; head[u] = tot++;
}
bool vis[N] ;
int cnt;
int bl[N], in[N];
int d[N] ;
vector <int> block[N];
void dfs(int u) {
vis[u] = 1;
bl[u] = cnt;
block[cnt].push_back(u) ;
for(int i = head[u]; i != -1; i = e[i].next) {
int v = e[i].v;
if(vis[v]) continue ;
dfs(v) ;
}
}
int main() {
ios::sync_with_stdio(false); cin.tie(0);
cin >> n >> r >> p >> s;
memset(head, -1, sizeof(head)) ;
for(int i = 1; i <= r; i++) {
int a, b, c;
cin >> a >> b >> c;
adde(a, b, c);
adde(b, a, c);
}
for(int i = 1; i <= n; i++) {
if(!vis[i]) {
cnt++;
dfs(i) ;
}
}
for(int i = 1; i <= p; i++) {
int a, b, c;
cin >> a >> b >> c;
adde(a, b, c) ;
in[bl[b]]++;
}
memset(vis, 0, sizeof(vis)) ;
memset(d, 0x7f, sizeof(d)) ;
d[s] = 0;
queue <int> Q;
Q.push(bl[s]) ;
for(int i = 1; i <= cnt; i++) if(!in[i]) Q.push(i) ;
priority_queue <node> q;
while(!Q.empty()) {
int blo = Q.front();Q.pop() ;
for(auto u : block[blo])
q.push(node{d[u], u}) ;
while(!q.empty()) {
node now = q.top(); q.pop();
int u = now.u;
if(vis[u]) continue ;
vis[u] = 1;
for(int i = head[u]; i != -1; i = e[i].next) {
int v = e[i].v;
if(d[v] > d[u] + e[i].w) {
d[v] = d[u] + e[i].w;
if(bl[v] == bl[u]) q.push(node{d[v],v}) ;
}
if(bl[v] != bl[u] && (--in[bl[v]]) == 0) Q.push(bl[v]) ;
}
}
}
for(int i = 1; i <= n; i++) {
if(d[i] > INF) cout << "NO PATH" << '\n' ;
else cout << d[i] << '\n' ;
}
return 0;
}