题目
分析
1.
3. 时间复杂度分析
每个连通块跑一遍dij_heap, m1logn1 + m2logn2 + … + milogni, 我们将他放缩一下 < (m1+m2+…+mi)logn = mlogn
AC代码
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
typedef pair<int, int> pii;
const int N = 25010, M = 200010, inf = 0x3f3f3f3f;
int n, mr, ms, s;
int h[N], w[M], e[M], ne[M], idx;
int id[N]; // 记录每个点属于的连通分量
int cnt; // 连通分量的数量
int dis[N];
bool st[N];
queue<int> q;
vector<int> block[N]; // 连通块数组
int d[N]; // 连通块的入度数组
void add (int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
void dij_heap(int u)
{
priority_queue<pii, vector<pii>, greater<pii>> pq;
for(int i = 0 ; i < block[u].size() ; i ++ )
pq.push({dis[block[u][i]], block[u][i]});
/*
上一个连通块是由起点拓展出来的最短距离,现连通块的起点也是由是一个连通块的点拓展出来的,所以距离也是到起点的距离
所以拓展出来的距离都是到起点的距离
*/
while(pq.size())
{
auto t = pq.top();
pq.pop();
int x = t.first, y = t.second;
if(st[y]) continue;
st[y] = 1;
for(int i = h[y] ; i != -1 ; i = ne[i])
{
int j = e[i];
if(dis[j] > x + w[i])
{
dis[j] = x + w[i];
if(id[j] == id[y]) pq.push({dis[j], j}); // 同一连通块才放进优先队列,防止了出现负权边, dijkstra无法解决
}
if (id[j] != id[y] && -- d[id[j]] == 0) q.push(id[j]);
}
}
}
void top_sort()
{
// memset整张图就将其串成了一张单源最短路的图
memset(dis, 0x3f, sizeof dis);
dis[s] = 0;
for(int i = 1 ; i <= cnt ; i ++ )
if(!d[i])
q.push(i);
while(q.size())
{
int t = q.front();
q.pop();
dij_heap(t);
}
}
void dfs (int u, int v) // 标记每个点所属的连通分量的编号
{
id[u] = v;
block[v].push_back(u);
for(int i = h[u] ; i != -1 ; i = ne[i])
{
int j = e[i];
if(!id[j]) dfs(j, v);
}
}
int main ()
{
scanf("%d%d%d%d", &n, &mr, &ms, &s);
memset(h, -1, sizeof h);
for(int i = 0 ; i < mr ; i ++ )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
add(b, a, c);
}
for(int i = 1 ; i <= n ; i ++ )
if(!id[i])
dfs(i, ++ cnt);
for(int i = 0 ; i < ms ; i ++ )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
d[id[b]] ++ ;
}
top_sort();
for(int i = 1 ; i <= n ; i ++ )
if(dis[i] > inf / 2)
puts("NO PATH");
else printf("%d\n", dis[i]);
return 0;
}