準備投身於ACM的潮流中,失蹤人口迴歸啦!
這個題目的思路還是非常有趣的,因爲我們可以注意到,兩個可能成爲答案興趣點之間的最短路不應該經過了第三個點,如果經過了,顯然和第三個點之間的最短路會更小,則原來的兩個點之間不應該成爲答案。
考慮到這一點,我們可以想到建枚舉每一條邊,找到這一條邊的兩個端點分別最近的興趣點,一個在正圖上,一個在反圖上,若二者不同,則這兩個距離加上這條邊的長度就是可能的答案。
這樣我們只要在正反圖上分別跑dij,並且記錄每個點到達的最近興趣點是哪個就行。
同時我們要注意到,在跑正反圖的時候,需要將所有的興趣點看作一個集合,以這個集合爲起點進行dij,這一個問題可以使用超級源點解決。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<map>
#include<vector>
#include<ctime>
#include<bitset>
#define int long long
using namespace std;
int t;
int n,m,k;
int p[2];
int head[200005][2];
struct edge{
int to;
int v;
int f;
int ne;
}ed[500005][2];
int dis[500005][2];
int col[500005][2];
int in[500005];
int x,y,z;
void add(int f,int to,int v,int id){
ed[++p[id]][id].to=to;
ed[p[id]][id].f=f;
ed[p[id]][id].ne=head[f][id];
head[f][id]=p[id];
ed[p[id]][id].v=v;
return ;
}
struct st{
int id;
int dis;
friend bool operator <(st x,st y){
return x.dis>y.dis;
}
};
priority_queue<st> q;
int vis[1000005];
void dij(int id){
while(!q.empty()) q.pop();
for(int i=0;i<=n;++i)
dis[i][id]=100000000000000000;
for(int i=1;i<=k;++i){
dis[in[i]][id]=0;
col[in[i]][id]=in[i];
q.push((st){in[i],0});
}
for(int i=1;i<=n;++i)
vis[i]=0;
while(!q.empty()){
st tem=q.top();
q.pop();
if(vis[tem.id]) continue;
vis[tem.id]=1;
for(int i=head[tem.id][id];i;i=ed[i][id].ne){
int to=ed[i][id].to;
if(dis[to][id]>dis[tem.id][id]+ed[i][id].v){
dis[to][id]=dis[tem.id][id]+ed[i][id].v;
col[to][id]=col[tem.id][id];
q.push((st){to,dis[to][id]});
}
}
}
}
signed main(){
scanf("%lld",&t);
while(t--){
memset(head,0,sizeof(head));
p[0]=p[1]=0;
scanf("%lld%lld%lld",&n,&m,&k);
for(int i=1;i<=m;++i){
scanf("%lld%lld%lld",&x,&y,&z);
add(x,y,z,0);
add(y,x,z,1);
}
for(int i=1;i<=k;++i){
scanf("%lld",&in[i]);
}
dij(0);
dij(1);
int ans=100000000000000000;
for(int i=1;i<=p[0];++i){
int u=ed[i][0].f;
int v=ed[i][0].to;
if(!col[u][0]||!col[v][1]) continue;
if(col[u][0]==col[v][1]) continue;
ans=min(ans,dis[u][0]+dis[v][1]+ed[i][0].v);
}
printf("%lld\n",ans);
}
return 0;
}