[GX/GZOI2019]旅行者(dijkstra)

二进制分组SB做法没意思还难写还可能会被卡常其实是我不会写。用一种比较优秀的O(Tnlogn)做法,只需要做2次dijkstra。对原图做一次、对反图做一次,然后记录每个点的最短路是从k个源点中的哪个转移过来的。然后枚举每条边,若两边转移过来的源点不同,则用d1+w[i]+d2来更新答案即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+7,M=5e5+7;
struct node{int u;ll d;};
bool operator<(node a,node b){return a.d>b.d;}
int n,m,k,tot,a[N],vis[N],hd[N],v[M],nxt[M],w[M],st[M],ed[M],len[M],c[2][N];
ll ans,d[2][N];
void adde(int x,int y,int z){v[++tot]=y,nxt[tot]=hd[x],w[tot]=z,hd[x]=tot;}
void dijkstra(ll*d,int*c)
{
    priority_queue<node>q;
    for(int i=1;i<=n;i++)d[i]=1e18,vis[i]=0;
    for(int i=1;i<=k;i++)d[a[i]]=0,c[a[i]]=a[i],q.push((node){a[i],0});
    while(!q.empty())
    {
        int u=q.top().u;q.pop();
        if(vis[u])continue;
        vis[u]=1;
        for(int i=hd[u];i;i=nxt[i])
        if(d[v[i]]>d[u]+w[i])d[v[i]]=d[u]+w[i],c[v[i]]=c[u],q.push((node){v[i],d[v[i]]});
    }
}
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&n,&m,&k);
        for(int i=1;i<=n;i++)hd[i]=0;tot=0;
        for(int i=1,x,y,z;i<=m;i++)
        {
            scanf("%d%d%d",&x,&y,&z);
            if(x==y)i--,m--;
            else adde(x,y,z),st[i]=x,ed[i]=y,len[i]=z;
        }
        for(int i=1;i<=k;i++)scanf("%d",&a[i]);
        dijkstra(d[0],c[0]);
        for(int i=1;i<=n;i++)hd[i]=0;tot=0;
        for(int i=1;i<=m;i++)adde(ed[i],st[i],len[i]);
        dijkstra(d[1],c[1]);
        ans=1e18;
        for(int i=1;i<=m;i++)
        if(c[0][st[i]]&&c[1][ed[i]]&&c[0][st[i]]!=c[1][ed[i]])
        ans=min(ans,d[0][st[i]]+d[1][ed[i]]+len[i]);
        printf("%lld\n",ans);
    }
}
View Code

 

posted @ 2019-05-13 21:49  hfctf0210  阅读(203)  评论(0编辑  收藏  举报