一.题目链接:
道路与航线
二.题目大意:
T 个点,R 条双向边,P 条单向边.
其中双向边权值均为正,单向边权值可为负.
保证如果存在 a 到 b 的路径,则不存在 b 到 a 的路径.
求 S 到所有点的最短距离.
三.分析:
现场赛的时候没发现是蓝书原题...
直接 SPFA 会 TLE...
由于题目给出的特殊条件:双向边权值为正,单向边权值可负,并且单向边不存在环.
如果只把双向边插入图中,会得到若干个连通块,把每个块看成一个点,再加入单向边,则变成了 DAG.
由此我们可以先拓扑排序,在拓扑排序里面跑块内的 dijkstra. (由于要更新入度,SPFA 便不再适用.)
注意:由于有负权单向边的存在,最后答案判定的时候,不可以写 dis == inf.
因为有可能 S 无法到达的点被其他点更新.
四.代码实现:
#include <bits/stdc++.h>
using namespace std;
const int M = 25000;
const int N = 50000 * 3;
const int inf = 0x3f3f3f3f;
int T, R, P, S;
int cnt;
int head[M + 5];
struct node
{
int v, w, nx;
}Edge[N + 5];
int dis[M + 5];
bool vis[M + 5];
int in[M + 5];
int tot, block[M + 5];
void init()
{
tot = cnt = 0;
for(int i = 1; i <= T; ++i)
{
vis[i] = 0;
head[i] = -1;
dis[i] = inf;
in[i] = 0;
block[i] = 0;
}
}
void add(int u, int v, int w)
{
Edge[cnt].v = v;
Edge[cnt].w = w;
Edge[cnt].nx = head[u];
head[u] = cnt++;
}
void dfs(int u)
{
for(int i = head[u]; ~i; i = Edge[i].nx)
{
int v = Edge[i].v;
if(!block[v])
{
block[v] = tot;
dfs(v);
}
}
}
queue <int> topo_q;
priority_queue < pair<int, int> > dij_q;
void dijkstra(int s)
{
for(int i = 1; i <= T; ++i)
{
if(block[i] == s) dij_q.push(make_pair(-dis[i], i));
}
while(!dij_q.empty())
{
int u = dij_q.top().second;
dij_q.pop();
if(vis[u]) continue;
vis[u] = 1;
for(int i = head[u]; ~i; i = Edge[i].nx)
{
int v = Edge[i].v;
if(dis[v] > dis[u] + Edge[i].w)
{
dis[v] = dis[u] + Edge[i].w;
if(block[v] == s) dij_q.push(make_pair(-dis[v], v));
}
if(block[v] != s && !--in[block[v]]) topo_q.push(block[v]);
}
}
}
void topo_sort()
{
for(int i = 1; i <= tot; ++i)
{
if(!in[i]) topo_q.push(i);
}
while(!topo_q.empty())
{
int u = topo_q.front();
topo_q.pop();
dijkstra(u);
}
}
int main()
{
scanf("%d %d %d %d", &T, &R, &P, &S);
init();
for(int i = 1, u, v, w; i <= R; ++i)
{
scanf("%d %d %d", &u, &v, &w);
add(u, v, w), add(v, u, w);
}
for(int i = 1; i <= T; ++i)
{
if(!block[i])
{
block[i] = ++tot;
dfs(i);
}
}
for(int i = 1, u, v, w; i <= P; ++i)
{
scanf("%d %d %d", &u, &v, &w);
add(u, v, w);
++in[block[v]];
}
dis[S] = 0;
topo_sort();
for(int i = 1; i <= T; ++i)
{
if(dis[i] > 1e9) printf("NO PATH\n");
else printf("%d\n", dis[i]);
}
return 0;
}