假设我们把特殊点分成 A,B 两个集合,新建 s 连 A 集合的所有点,边权 0 ,新建 t 连接 B 集合里的所有点,边权 0 ,那么 s 到 t 的最短路就是 A,B 集合点之间的最短路的最小值。
那么对于 k 个特殊点,我们枚举二进制里的第 i 位,把二进制第 i 位是 0 的点放在 A , 1 的点放在 B ,用以上方法跑一个最短路。
然后跑 log n 次最短路之后,所有最短路的最小值就是最终答案。
原理是,假设 k 个特殊点里最近的是 x 和 y ,那么 x 和 y 一定有一个二进制位不一样,那么他们肯定在那次分组的时候被放进了不同的集合,从而肯定被算进了最后的答案之中最短路。
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long LL;
typedef pair<LL,LL> PLL;
const int N=1e5+10,M=5e5+10;
int a[N];
int n,m,k,s,t;
LL dist[N];
vector<vector<PLL>> edges;
vector<vector<PLL>> nw;
LL dijkstra(){memset(dist,0x3f,sizeof(dist));auto cmp=[](PLL& x,PLL& y){return x.second>y.second;};priority_queue<PLL,vector<PLL>,decltype(cmp)> heap(cmp);heap.push({s,0});dist[s]=0;while(heap.size()){auto tmp=heap.top();int u=tmp.first;LL d=tmp.second;heap.pop();if(d>dist[u])continue;for(auto &[v,w]:nw[u]){if(dist[v]>dist[u]+w){dist[v]=dist[u]+w;heap.push({v,dist[v]});}}}return dist[t];
}
void solve(){cin>>n>>m>>k;edges=vector<vector<PLL>>(n+5,vector<PLL>(0));for(int i=1;i<=m;i++){LL x,y,z;cin>>x>>y>>z;edges[x].push_back({y,z});}for(int i=1;i<=k;i++){cin>>a[i];}s=n+1;t=n+2;LL ans=~0ull>>1;for(int i=0;(1<<i)<=k;i++){nw=edges;for(int j=1;j<=k;j++){if((j>>i)&1)nw[s].push_back({a[j],0});elsenw[a[j]].push_back({t,0});}ans=min(ans,dijkstra());nw=edges;for(int j=1;j<=k;j++){if((j>>i)&1)nw[a[j]].push_back({t,0});elsenw[s].push_back({a[j],0});}ans=min(ans,dijkstra());}cout<<ans<<endl;
}
int main(){cin.tie(nullptr)->sync_with_stdio(false);int T;cin>>T;while(T--){solve();}return 0;
}