联赛模拟测试23 C. 最小距离 多源最短路

题目描述

分析

以所有特殊点为起点跑多源最短路,并且记录每个点是由哪个源点拓展的。
然后枚举所有边,如果边的两端是由不同源点拓展的,就更新这两个点的答案。
不难证明,对于源点 \(i\),由 \(i\) 拓展的点 \(j\) 以及与 \(j\) 相邻且不由 \(i\) 拓展的点 \(k\),
如果 \(i\) 的最优路径从 \(j\) 走到了 \(k\),那么走到拓展 \(k\) 的源点是最优的。因此这个做
法是正确的。
时间复杂度 \(O((n+m)logn)\)

代码

#include<cstdio>
#include<queue>
#include<algorithm>
#include<cstring>
typedef long long ll;
const int maxn=1e6+5;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
int head[maxn],tot=1;
struct asd{
	int from,to,next,val;
}b[maxn];
void ad(int aa,int bb,int cc){
	b[tot].from=aa;
	b[tot].to=bb;
	b[tot].val=cc;
	b[tot].next=head[aa];
	head[aa]=tot++;
}
int n,m,p,x[maxn],f[maxn];
ll dis[maxn],ans[maxn];
bool vis[maxn];
struct jie{
	int num;
	ll jl;
	jie(int aa,ll bb){
		num=aa,jl=bb;
	}
	bool operator < (const jie& A)const{
		return jl>A.jl;
	}
};
void dij(){
	std::priority_queue<jie> q;
	memset(dis,0x3f,sizeof(dis));
	for(int i=1;i<=p;i++){
		dis[x[i]]=0;
		f[x[i]]=x[i];
		q.push(jie(x[i],0));
	}
	while(!q.empty()){
		int now=q.top().num;
		q.pop();
		if(vis[now]) continue;
		vis[now]=1;
		for(int i=head[now];i!=-1;i=b[i].next){
			int u=b[i].to;
			if(dis[u]>dis[now]+(ll)b[i].val){
				dis[u]=dis[now]+(ll)b[i].val;
				f[u]=f[now];
				q.push(jie(u,dis[u]));
			}
		}
	}
}
int main(){
	memset(head,-1,sizeof(head));
	n=read(),m=read(),p=read();
	for(int i=1;i<=p;i++){
		x[i]=read();
	}
	for(int i=1;i<=m;i++){
		int aa,bb,cc;
		aa=read(),bb=read(),cc=read();
		ad(aa,bb,cc);
		ad(bb,aa,cc);
	}
	dij();
	memset(ans,0x3f,sizeof(ans));
	for(int i=1;i<tot;i+=2){
		int aa=b[i].from,bb=b[i].to,cc=b[i].val;
		if(f[aa]!=f[bb]){
			ans[f[aa]]=std::min(ans[f[aa]],dis[aa]+dis[bb]+cc);
			ans[f[bb]]=std::min(ans[f[bb]],dis[aa]+dis[bb]+cc);
		}
	}
	for(int i=1;i<=p;i++){
		printf("%lld ",ans[x[i]]);
	}
	printf("\n");
	return 0;
}
posted @ 2020-10-26 17:26  liuchanglc  阅读(93)  评论(0编辑  收藏  举报