20191030 练习:多源最短路

第二道黑题祭~~~

P5304 [GXOI/GZOI2019]旅行者

P5304 [GXOI/GZOI2019]旅行者
题目描述
J J J 国有 n n n 座城市,这些城市之间通过 m m m 条单向道路相连,已知每条道路的长度。
一次,居住在 J J J 国的 R a i n b o w Rainbow Rainbow 邀请 V a n i Vani Vani 来作客。不过,作为一名资深的旅行者, V a n i Vani Vani 只对 J J J 国的 k k k 座历史悠久、自然风景独特的城市感兴趣。
为了提升旅行的体验, V a n i Vani Vani 想要知道他感兴趣的城市之间「两两最短路」的最小值(即在他感兴趣的城市中,最近的一对的最短距离)。
也许下面的剧情你已经猜到了—— V a n i Vani Vani 这几天还要忙着去其他地方游山玩水,就请你帮他解决这个问题吧。

输入格式
每个测试点包含多组数据,第一行是一个整数 T T T,表示数据组数。注意各组数据之间是互相独立的。
对于每组数据,第一行包含三个正整数 n , m , k n,m,k n,m,k 表示 J 国的 nn 座城市(从 1 ∼ n 1 \sim n 1n 编号), m m m 条道路, V a n i Vani Vani 感兴趣的城市的个数 k k k
接下来 m m m 行,每行包括 3 3 3 个正整数 x , y , z x,y,z x,y,z,表示从第 x x x 号城市到第 y y y 号城市有一条长度为 z z z 的单向道路。注意 x , y x,y x,y 可能相等,一对 x , y x,y x,y 也可能重复出现。
接下来一行包括 k k k 个正整数,表示 V a n i Vani Vani 感兴趣的城市的编号。

输出格式
输出文件应包含 T T T 行,对于每组数据,输出一个整数表示 k k k 座城市之间两两最短路的最小值。

输入输出样例
输入
2
6 7 3
1 5 3
2 3 5
1 4 3
5 3 2
4 6 5
4 3 7
5 6 4
1 3 6
7 7 4
5 3 10
6 2 7
1 2 6
5 4 2
4 3 4
1 7 3
7 2 4
1 2 5 3
输出
5
6

说明/提示
样例解释
对于第一组数据, 1 1 1 3 3 3 最短路为 5 5 5 1 1 1 6 6 6 最短路为 7 7 7 3 , 6 3,6 3,6 无法到达,所以最近的两点为 1 , 3 1,3 1,3,最近的距离为 5 5 5
对于第二组数据, 1 1 1 2 2 2 最短路为 6 6 6 5 5 5 3 3 3 最短路为 6 6 6;其余的点均无法互相达,所以最近的两点为 1 , 2 1,2 1,2 5 , 3 5,3 5,3,最近的距离为 6 6 6

数据范围
2 ≤ k ≤ n , 1 ≤ x , y ≤ n , 1 ≤ z ≤ 2 × 1 0 9 , T ≤ 52 ≤ k ≤ n , 1 ≤ x , y ≤ n , 1 ≤ z ≤ 2 × 1 0 9 , T ≤ 5 。 2 \le k \le n,1 \le x,y \le n,1 \le z \le 2 \times 10^9,T ≤52≤k≤n,1≤x,y≤n,1≤z≤2×10^9 ,T≤5。 2kn,1x,yn,1z2×109,T52kn,1x,yn,1z2×109,T5
测试点编号 n n n 的规模 m m m 的规模 约定
1 ≤ 1 , 000 ≤ 5 , 000 \le 1,000 \le 5,000 1,0005,000
2 ≤ 1 , 000 ≤ 5 , 000 \le 1,000 \le 5,000 1,0005,000
3 ≤ 100 , 000 ≤ 500 , 000 \le 100,000 \le 500,000 100,000500,000 保证数据为有向无环图
4 ≤ 100 , 000 ≤ 500 , 000 \le 100,000 \le 500,000 100,000500,000 保证数据为有向无环图
5 ≤ 100 , 000 ≤ 500 , 000 \le 100,000 \le 500,000 100,000500,000 保证数据为有向无环图
6 ≤ 100 , 000 ≤ 500 , 000 \le 100,000 \le 500,000 100,000500,000
7 ≤ 100 , 000 ≤ 500 , 000 \le 100,000 \le 500,000 100,000500,000
8 ≤ 100 , 000 ≤ 500 , 000 \le 100,000 \le 500,000 100,000500,000
9 ≤ 100 , 000 ≤ 500 , 000 \le 100,000 \le 500,000 100,000500,000
10 ≤ 100 , 000 ≤ 500 , 000 \le 100,000 \le 500,000 100,000500,000

思路:
类似20191029 csp-s模拟T3(多源最短路)
但是为了避免对顶的情况,要在正反两个图上各跑一次 D i j k s t r a Dijkstra Dijkstra

代码:

#include<bits/stdc++.h>
using namespace std;
#define in Read()

long long in{
	long long s=0,f=1;char x;
	for(x=getchar();!isdigit(x);x=getchar())    if(x=='-')  f=-1;
	for( ;isdigit(x);x=getchar())   s=(s<<1)+(s<<3)+(x&15);
	return s*f;
}

const long long A=1e6+5;
long long t;
long long n,m,k;
long long u,v,w;
long long xx[A],yy[A],ww[A];
long long head[A],tot=1;
struct Road{
	long long nex,to,w;
}road[A];
inline void ljb(long long x,long long y,long long w){road[++tot]={head[x],y,w};head[x]=tot;}
long long p[A];
long long res=1e18;
long long s[A][2],f[A][2];
bool ex[A][2];

inline void djk(long long d){
	for(long long i=1;i<=n;i++)   s[i][d]=1e18,ex[i][d]=0;
	priority_queue <pair<long long,long long> > q;
	for(long long i=1;i<=k;i++){
		s[p[i]][d]=0,f[p[i]][d]=p[i];
		q.push(make_pair(0,p[i]));
	}
	while(!q.empty()){
		long long x=q.top().second;q.pop();
		if(ex[x][d])    continue;
		ex[x][d]=1;
		for(long long y=head[x];y;y=road[y].nex){
			long long z=road[y].to,w=road[y].w;
			if(s[z][d]>s[x][d]+w){
				s[z][d]=s[x][d]+w,f[z][d]=f[x][d];
				q.push(make_pair(-s[z][d],z));
			}
		}
	}
}

inline void clean(){
	for(long long i=1;i<=n;i++)head[i]=0;
	tot=0;
}

signed main(){
	t=in;
	while(t--){
  		res=1e18;
		n=in,m=in,k=in;
		clean();
		for(long long i=1;i<=m;i++){
			u=in,v=in,w=in;
			xx[i]=u,yy[i]=v,ww[i]=w;
			ljb(u,v,w);
		}
		for(long long i=1;i<=k;i++)
			p[i]=in;
		djk(0);
		clean();
		for(long long i=1;i<=m;i++)
			ljb(yy[i],xx[i],ww[i]);
		djk(1);
		for(long long i=1;i<=m;i++){
			long long x=xx[i],y=yy[i],w=ww[i];
			if(f[x][0]&&f[y][1]&&f[x][0]!=f[y][1])
					res=min(res,s[x][0]+s[y][1]+w);
		}
		printf("%lld\n",res);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值