P3008 [USACO11JAN] Roads and Planes G(dij + 拓扑排序)
发现单向航线存在负边权,不能直接跑 dij
而双向的道路保证为正边权,可以在道路中跑 dij,想到把道路进行类似“缩点”的操作,把直接相连的道路看做连通快
那么在连通快这样的团之间,有航线相连,且航线满足一个很好的性质:若存在 x -> y,则不可能存在 y -> x,即无环
那团与航线所成的图就是一个含负边权的 DAG
后来发现 DAG 上求单源最短路,可以直接做一遍拓扑排序扫描 \(O(n+m)\)
code
inline void topsort()
{
memset(dist, 0x3f, sizeof(dist));
for (int i = 1; i <= n; i ++)
if (!in[i]) q.push(i);
while (!q.empty())
{
int x = q.front(); q.pop();
for (int i = h[x]; i; i = e[i].next)
{
int y = e[i].to, w = e[i].w;
dist[y] = min(dist[y], dist[x] + w);
if (--in[y] == 0) q.push(y);
}
}
}
所以,思路就很直观了,对道路预处理出连通快,总的对团与航线层次做拓扑排序,再在其中每次的连通快内的层次做 dij,不过实际实现还有很多细节要注意
算法复杂度瓶颈就是 dij,整个做法也就是 \(O(m\log n)\)
#include <bits/stdc++.h>
#define re register int
using namespace std;
typedef pair<int, int> pii;
const int N = 3e4 + 10, M = 2e5 + 10, inf = 0x3f3f3f3f;
struct edge
{
int to, w, next;
}e[M];
int top, h[N];
int dist[N], in[N], id[N], idx;
int n, mr, ml, s;
bool vis[N];
vector<int> block[N];
queue<int> tp;
priority_queue< pii, vector<pii>, greater<pii> > q;
inline void add(int x, int y, int w)
{
e[++ top] = (edge){y, w, h[x]};
h[x] = top;
}
void dfs(int u, int uid)
{
id[u] = uid;
block[uid].push_back(u);
for (int i = h[u]; i; i = e[i].next)
{
int v = e[i].to;
if (!id[v])
dfs(v, uid);
}
}
inline void dijkstra(int xid)
{
for (re i = 0; i < block[xid].size(); i ++)
{
int x = block[xid][i];
q.push(make_pair(dist[x], x));
}
while (!q.empty())
{
int x = q.top().second; q.pop();
if (vis[x]) continue;
vis[x] = true;
for (re i = h[x]; i; i = e[i].next)
{
int y = e[i].to, w = e[i].w;
if (dist[y] > dist[x] + w)
{
dist[y] = dist[x] + w;
if (id[x] == id[y])
q.push(make_pair(dist[y], y));
}
if (id[x] != id[y] && -- in[id[y]] == 0)
tp.push(id[y]);
}
}
}
inline void topsort()
{
for (re i = 1; i <= n; i ++) dist[i] = inf;
dist[s] = 0;
for (re i = 1; i <= idx; i ++)
if (in[i] == 0) tp.push(i);
while (!tp.empty())
{
int x = tp.front(); tp.pop();
dijkstra(x);
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n >> mr >> ml >> s;
while (mr --)
{
int a, b, c; cin >> a >> b >> c;
add(a, b, c);
add(b, a, c);
}
for (int i = 1; i <= n; i ++)
if (!id[i])
{
idx ++;
dfs(i, idx);
}
while (ml --)
{
int a, b, c; cin >> a >> b >> c;
add(a, b, c);
in[id[b]] ++;
}
topsort();
for (re i = 1; i <= n; i ++)
if (dist[i] > N * 10000) cout << "NO PATH\n";
else cout << dist[i] << '\n';
return 0;
}

浙公网安备 33010602011771号