洛谷P3008 [USACO11JAN]Roads and Planes G

题目链接:P3008 [USACO11JAN]Roads and Planes G
题目大意:
给定一张图,有 \(T\) 个点,\(R\) 条双向边和 \(P\) 条单向边,保证每条单向边 \(u\rightarrow v\) 不存在道路使得 \(v\rightarrow u\) ,其中单向边的长度可能为负数,给定起点 \(S\) 求单源最短路。
\(1\leq T\leq 25000\)\(1\leq R,P\leq 50000\)

思路:
图中有负权边,据我所知出题人卡了SPFA,所以不要想了。

注意到只有单向边有负权,把它们拉出来,当图中只剩下双向边时,会产生若干个连通块,把单向边加回去时,这些块重新连起来,考虑将其缩点,那么单向边组成的

就变成了一张DAG,DAG上单源最短路可以直接拓扑,不受负权影响,这启发我们建DAG,点内跑Dijkstra,图上跑拓扑。时间复杂度 \(O(T+P+RlogT)\)

细节

  • 由于有负边权,dis数组上赋的极大值可能会被削小,判"NO PATH"的时候不能直接"==0x3f3f3f3f"。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#define N 25010
#define E 50010
#define PII pair<int,int>
#define fr first
#define sc second
#define mk make_pair
using namespace std;
int head[N],nxt[E*3],to[E*3];
int cnt,val[E*3],conn[N];
vector<int> tot[N];
int deg[N],dis[N];
bool vis[N];
inline int read(){
    int s=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
    return s*w;
}
void init(){
    memset(head,-1,sizeof(head));
    memset(dis,0x3f,sizeof(dis));
    cnt=-1;
}
void add_e(int a,int b,int c,bool id){
    nxt[++cnt]=head[a];
    head[a]=cnt;
    to[cnt]=b;
    val[cnt]=c;
    if(id)add_e(b,a,c,0);
}
void dfs(int x,int id){
    vis[x]=true;
    conn[x]=id;
    tot[id].push_back(x);
    for(int i=head[x];~i;i=nxt[i]){
        if(vis[to[i]])continue;
        dfs(to[i],id);
    }
}
queue<int> topo;
priority_queue<PII,vector<PII>,greater<PII> > q;
bool Min(int &a,int b){
    if(a<=b)return false;
    a=b;return true;
}
void dijkstra(int x){
    for(int i=0;i<tot[x].size();i++){
        q.push(mk(dis[tot[x][i]],tot[x][i]));
    }
    while(!q.empty()){
        int cur=q.top().sc; q.pop();
        if(vis[cur])continue;
        vis[cur]=true;
        for(int i=head[cur];~i;i=nxt[i]){
            int v=to[i];
            if(Min(dis[v],dis[cur]+val[i]))
                if(conn[cur]==conn[v])q.push(mk(dis[v],v));
            if(conn[cur]!=conn[v]&&--deg[conn[v]]==0)topo.push(conn[v]);
        }
    }
}
int t,r,p,s;
int main(){
    cin>>t>>r>>p>>s;
    int a,b,c;
    init();
    for(int i=0;i<r;i++){
        a=read(),b=read(),c=read();
        add_e(a,b,c,1);
    }
    for(int i=1;i<=t;i++) if(!vis[i])dfs(i,i);
    for(int i=0;i<p;i++){
        a=read(),b=read(),c=read();
        deg[conn[b]]++;
        add_e(a,b,c,0);
    }
    memset(vis,false,sizeof(vis));
    dis[s]=0;
    for(int i=1;i<=t;i++)
        if(conn[i]==i&&!deg[i])topo.push(i);
    while(!topo.empty()){
        int now=topo.front(); topo.pop();
        dijkstra(now);
    }
    for(int i=1;i<=t;i++){
        if(dis[i]>500000000)puts("NO PATH");
        else printf("%d\n",dis[i]);
    }
    return 0;
}
posted @ 2020-12-24 21:33  Neal_lee  阅读(146)  评论(0编辑  收藏  举报