【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;
}
posted @ 2020-01-29 17:40  MagicDuck  阅读(212)  评论(0编辑  收藏  举报