1. 程式人生 > 實用技巧 >洛谷P3008 [USACO11JAN]Roads and Planes G

洛谷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;
}