【BZOJ2200】道路与航线
题目链接:https://www.acwing.com/problem/content/344/
题目大意:给定一张有向图 , 其中可分为若干个连通块 , 每个连通块内的道路均为双向 , 且权值为正 , 联通块间的道路为单向 , 权值可为负数 , 这若干个连通块组成一个DAG , 求起点 \(s\) 到各个节点的最短路径
solution
显然这是一个单元最短路模板 , 但边权有为负 , 用SPFA会被卡掉 , 但可以利用题目中的一些性质来解决
对于每个连通块内 , 可以用dijstra来求出点之间的最短路径 , 连通块之间由于构成了一个DAG , 可以用拓扑来求最短路径
具体地 , 可以构建一个队列 , 每次加入总入度为 0 的联通块 , 每次取出队首的连通块 , 在块内跑dijstra , 与此同时把这个连通块所相邻的各个连通块的总入度减去 1 , 若发现入度减为 0 , 则把这个连通块入队
复杂度 : \(O(mlogn)\)
code
#include<bits/stdc++.h>
using namespace std;
template <typename T> inline void read(T &FF) {
int RR = 1; FF = 0; char CH = getchar();
for(; !isdigit(CH); CH = getchar()) if(CH == '-') RR = -RR;
for(; isdigit(CH); CH = getchar()) FF = FF * 10 + CH - 48;
FF *= RR;
}
inline void file(string str) {
freopen((str + ".in").c_str(), "r", stdin);
freopen((str + ".out").c_str(), "w", stdout);
}
const int N = 5e5 + 10;
int si, now, vis[N], fst[N], nxt[N], num[N], wi[N], n, x, y, s, rd[N];
void add(int u, int v, int w) {
nxt[++now] = fst[u], fst[u] = now, num[now] = v, wi[now] = w;
}
void dfs(int xi) {
for(int i = fst[xi]; i; i = nxt[i])
if(!vis[num[i]])
vis[num[i]] = vis[xi], dfs(num[i]);
}
int st[N], bk[N]; queue<int> q;
void dijstra(int xi) {
priority_queue<pair<int, int> > qi;
for(int i = 1; i <= n; i++)
if(vis[i] == xi)
qi.push(make_pair(-st[i], i));
while(!qi.empty()) {
int pi = qi.top().second; qi.pop();
if(bk[pi]) continue; bk[pi] = true;
for(int i = fst[pi]; i; i = nxt[i]) {
if(st[pi] + wi[i] < st[num[i]]) {
st[num[i]] = st[pi] + wi[i];
if(vis[pi] == vis[num[i]])
qi.push(make_pair(-st[num[i]], num[i]));
}
if(vis[pi] != vis[num[i]]) {
rd[vis[num[i]]]--;
if(!rd[vis[num[i]]]) q.push(vis[num[i]]);
}
}
}
}
int main() {
//file("");
int u, v, w;
read(n), read(x), read(y), read(s);
for(int i = 1; i <= x; i++)
read(u), read(v), read(w), add(u, v, w), add(v, u, w);
for(int i = 1; i <= n; i++)
if(!vis[i]) vis[i] = ++si, dfs(i);
for(int i = 1; i <= y; i++) {
read(u), read(v), read(w), add(u, v, w);
rd[vis[v]]++;
}
memset(st, 0x3f, sizeof(st)); st[s] = 0;
for(int i = 1; i <= si; i++)
if(rd[i] == 0) q.push(i);
//if(rd[vis[s]]) q.push(vis[s]);
while(!q.empty()) {
int pi = q.front(); q.pop();
dijstra(pi);
}
for(int i = 1; i <= n; i++)
if(st[i] >= 1e9) puts("NO PATH");
else cout << st[i] << endl;
return 0;
}