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();
}
posted @ 2020-06-06 09:56  Shiina_Mashiro  阅读(132)  评论(0编辑  收藏  举报