Luogu P5304 [GXOI/GZOI2019]旅行者
Link
二进制分组。每次选定一个二进制位,把特殊点按编号在这一位上的状态分为两半,跑两遍dij求出这两半之间的最短路。
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#include<functional>
using i64=long long;
using pi=std::pair<int,int>;
const int N=100007;const i64 inf=1ll<<60;
char ibuf[1<<25],*iS=ibuf;int n,is[N];i64 ans,dis[N];std::vector<pi>e[N];std::priority_queue<pi,std::vector<pi>,std::greater<pi>>q;
int read(){int x=0;while(isspace(*iS))++iS;while(isdigit(*iS))(x*=10)+=*iS++&15;return x;}
void solve(int d,int t)
{
memset(dis+1,0x3f,8*n);
for(int i=1;i<=n;++i) if(is[i]&&(i>>d&1)==t) q.emplace(dis[i]=0,i);
while(!q.empty())
{
int u=q.top().second;i64 d=q.top().first;q.pop();
if(d==dis[u]) for(auto[v,w]:e[u]) if(dis[v]>dis[u]+w) q.emplace(dis[v]=dis[u]+w,v);
}
for(int i=1;i<=n;++i) if(is[i]&&(~i>>d&1)==t) ans=std::min(ans,dis[i]);
}
void solve()
{
n=read(),ans=inf,memset(is+1,0,4*n);
int m=read(),k=read();
for(int i=1;i<=n;++i) e[i].clear();
for(int u,v,w;m;--m) u=read(),v=read(),w=read(),e[u].emplace_back(v,w);
while(k--) is[read()]=1;
for(int k=0;k<17;++k) solve(k,0),solve(k,1);
printf("%lld\n",ans);
}
int main()
{
fread(ibuf,1,1<<25,stdin);
for(int t=read();t;--t) solve();
}