洛谷P3008 [USACO11JAN]Roads and Planes G
阿新 • • 發佈:2020-12-24
題目連結: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; }