【图论】AcWing 342. 道路与航线 题目解答 (拓扑序+dijkstra)

传送门:https://www.acwing.com/problem/content/344/

吐槽:这就是一道大膜你模拟啊。
不得不说写作课的时候想题大有启发(雾

分析

看到这题有负权,想到用SPFA,可惜这题范围很大,完全被卡死了(然而有人用SPFA优化过了),故考虑别的做法。
大致思路:
道路联通的部分看成,那么航线则是通过有向边将每个块连起来的,故从整体上看,有向边和各个块组成了一个图。
在处理块的时候,可以在输入道路的时候建立,方法类似于对图进行染色。

而根据题意,这个图是DAG(有向无环图),可以考虑用拓扑序处理。
首先我们可以对这个DAG进行从源点所在块一遍深搜,然后将源点能够到达的其他块打上标记。

接下来我们只需对源点所在块以及它能够到达的其他块进行讨论就行了。(其他块不需要管)

类似于拓扑序的做法,
在处理航线的时候,维护一下入度。
然后我们先将源点所在块入队,将块中点全部丢进优先队列里面进行 dijkstra,如果dijktra过程中访问到了

  • 不是本块里面的点,需要对入度进行维护,当一个块(不是当前块)入度为 \(0\) 时,将它丢进队列里面。
  • 是本块的点,像dijktra一样处理就好了~

过程确实比较简单(大概),但是实现挺复杂的,建议自己先动手试试。

代码:

#include<bits/stdc++.h>
using namespace std;

typedef pair<int,int> PII;
const int INF=0x3f3f3f3f;
const int N=25005, M=5e4+5;
int n,p,r,s;
int in[N];
int col,id[N];
vector<int> block[N];
bool tag[N];
bool vis[N];
int d[N];
queue<int> q; // for topo block
priority_queue<PII,vector<PII>,greater<PII> > que; // for dijk()

struct Store{
	int u,v,w;
}store[M<<1];

struct node{
	int next,to,w;
}e[M<<1],ee[M<<1];

int cnt,h[N];
void addd(int u,int v){ee[cnt].to=v;ee[cnt].next=h[u];h[u]=cnt++;}

int head[N],tot;
void add(int u,int v,int w){e[tot].to=v;e[tot].w=w;e[tot].next=head[u];head[u]=tot++;}

void assign(int u){
	id[u]=col;
	block[col].push_back(u);
	for(int i=head[u];~i;i=e[i].next){
		int go=e[i].to;
		if(id[go]!=0) continue;
		assign(go);
	}
}

void dfs(int u){
	tag[u]=true;
	for(int i=h[u];~i;i=ee[i].next){
		int go=ee[i].to;
		//cerr<<go<<' ';
		if(tag[go]) continue;
		dfs(go);
	}
}

void dijk(int color){
	while(que.size()){
		auto hd=que.top(); que.pop();
		
		int ver=hd.second;
		if(vis[ver]) continue;
		vis[ver]=true;
		
		for(int i=head[ver];~i;i=e[i].next){
			int go=e[i].to;
			if(id[go]!=color) in[id[go]]--;
			if(!in[id[go]] && id[go]!=color) q.push(id[go]);
			
			if(d[go]>d[ver]+e[i].w){
				d[go]=d[ver]+e[i].w;
				if(id[go]==color) que.push({d[go],go});			
			}
		}
	}
}

void topo(){
	//for(int i=1;i<=col;i++) cerr<<in[id[i]];
	in[id[s]]=0;
	q.push(id[s]);
	
	while(q.size()){
		int pt=q.front(); q.pop();
		cerr<<pt<<' ';
		for(auto i:block[pt]) que.push({d[i],i});
		dijk(pt);
	}
}

int main(){
	memset(head,-1,sizeof head);
	memset(h,-1,sizeof h);
	
	cin>>n>>p>>r>>s;
	memset(d,0x3f,sizeof d);
	d[s]=0;
	
	while(p--){
		int u,v,w; cin>>u>>v>>w;
		add(u,v,w); add(v,u,w);
	}	
	
	for(int i=1;i<=n;i++)
		if(!id[i]) {
			col++;
			assign(i);
		}
	
	for(int i=1;i<=r;i++){
		int u,v,w;
		cin>>u>>v>>w;
		store[i]={u,v,w};
		addd(id[u],id[v]);
		//cerr<<id[u]<<' '<<id[v]<<endl;
	}
	
	dfs(id[s]);
	
	for(int i=1;i<=r;i++){
		int u=store[i].u, v=store[i].v, w=store[i].w;
		if(!tag[id[u]]) continue;
		//cerr<<"imsb";
		add(u,v,w);
		in[id[v]]++;
	}
	
	topo();
	
	for(int i=1;i<=n;i++)
		if(d[i]==INF) puts("NO PATH");
		else cout<<d[i]<<endl;
		
	return 0;
}

彩蛋

posted @ 2021-03-09 22:14  HinanawiTenshi  阅读(90)  评论(0编辑  收藏  举报