#include using namespace std; pair>> SteinerTree(const vector>> &Graph,const vector &X){ //頂点集合Xを含む全域木 O(N3^K+2^K(N+M)logN). //return {cost,辺Ui-Vi集合(Ui> dist(K2,vector(N,inf)); priority_queue,vector>,greater<>> Q; for(int i=0; i0; t=(t-1)&s){ int p = t,q = s^t; if(p < q) break; for(int i=0; i> edge; { int pos = min_element(dist.back().begin(),dist.back().end())-dist.back().begin(),S = K2-1; cost = dist.at(S).at(pos); if(cost == inf) return {inf,{}}; queue> SP; SP.push({S,pos}); while(SP.size()){ tie(S,pos) = SP.front(); SP.pop(); while(true){ bool cont = false; for(auto [to,w] : Graph.at(pos)){ if(dist.at(S).at(to)+w == dist.at(S).at(pos)){ edge.push_back({min(to,pos),max(to,pos)}); cont = true; pos = to; break; } } if(cont) continue; break; } if(dist.at(S).at(pos) > 0){ for(int T=(S-1)&S; T>0; T=(T-1)&S){ int p = T,q = S^T; if(dist.at(p).at(pos)+dist.at(q).at(pos) == dist.at(S).at(pos)){ SP.push({p,pos}),SP.push({q,pos}); break; } } } } } return {cost,edge}; } class UnionFind{ private: vector par,siz; public: UnionFind(int N){ par.resize(N,-1); siz.resize(N,1); } int root(int x){ //連結成分の代表頂点を返す. if(par.at(x) == -1) return x; else return par.at(x) = root(par.at(x)); } bool unite(int u, int v){ //u,vを連結する 連結してた->false,した->trueを返す. u = root(u),v = root(v); if(u == v) return false; if(siz.at(u) < siz.at(v)) swap(u,v); //Union by size. par.at(v) = u; siz.at(u) += siz.at(v); return true; } bool issame(int u, int v){ //同じ連結成分ならtrue. if(root(u) == root(v)) return true; else return false; } int size(int pos){return siz.at(root(pos));} //posの連結成分の大きさを返す. }; int main(){ ios_base::sync_with_stdio(false); cin.tie(nullptr); int N,M,T; cin >> N >> M >> T; vector>> Graph(N); vector> edge; for(int i=0; i> u >> v; u--; v--; int w; cin >> w; edge.push_back({w,u,v}); Graph.at(u).push_back({v,w}); Graph.at(v).push_back({u,w}); } vector X(T); for(auto &x : X) cin >> x,x--; if(T <= 17) cout << SteinerTree(Graph,X).first << endl; else{ vector isX(N); for(auto x : X) isX.at(x) = true; vector Y; for(int i=0; i