2019 ICPC 銀川 H. Delivery Route (強連通分量+拓撲+分塊最短路)
阿新 • • 發佈:2020-12-14
技術標籤:=====圖論=====連通圖
題鏈:https://nanti.jisuanke.com/t/42388
題意:一個有向圖,有雙向邊(邊權全為正),單向邊(邊權可能為負),求一個起點到其他點的最短路。
思路:有負權邊且卡spfa。注意題目中有一個非常重要的條件。
也就是環中不可能有負權邊。那麼,我們考慮tarjan縮點,那麼在一個強連通分量中的點之間的邊都是正權邊,那麼內部的最短路就能用Dijkstra了。
縮點後,圖變成一個樹,強連通分量之間的最短路就能用Topo排序求了。
注意,分塊是指一個強連通分量(一個縮點),一個強連通分量的處理。
#include <bits/stdc++.h> #define ll long long using namespace std; const int N = 3e4+10; const int M = 2e5+10; const int inf = 0x3f3f3f3f; int n,x,y,s; struct node{ int to,w,nex; }g[M]; struct Node{ int to,d; friend bool operator<(Node a,Node b){ return a.d>b.d; } }; int cnt,head[N]; int dis[N],in[N]; void add(int u,int v,int w){ g[cnt]=node{v,w,head[u]}; head[u]=cnt++; } int dfn[N],top,id,low[N],sta[N],c[N],cl,vis[N]; void init(){ cl=top=id=cnt=0; for(int i=1;i<=n;i++) dfn[i]=in[i]=0,dis[i]=inf,head[i]=-1; } vector<int> scc[N];//每個強連通分量中有那些點 //tarjan求強連通分量 void tarjan(int u){ low[u]=dfn[u]=++id; sta[++top]=u; vis[u]=1; for(int i=head[u];~i;i=g[i].nex){ int v=g[i].to; if(!dfn[v]){ tarjan(v); low[u]=min(low[u],low[v]); } else if(vis[v]) low[u]=min(low[u],dfn[v]); } if(dfn[u]==low[u]){ c[u]=++cl; scc[cl].push_back(u); while(sta[top]!=u){ c[sta[top]]=cl; scc[cl].push_back(sta[top]); vis[sta[top--]]=0; } vis[sta[top--]]=0; } } vector<int> ng[N];//縮點後的圖,一棵樹 unordered_map<int,unordered_map<int,int> > mp; //標記重邊 vector<int> ok[N];//強連通分量可以作起點(與另一強連通分量中的點相連的點,有可能更新最短路)的點 //建新圖,用來Topo和分塊處理 void getng(){ tarjan(s); for(int u=1;u<=n;u++){ if(!c[u]) continue; for(int i=head[u];~i;i=g[i].nex){ int v=g[i].to; if(!c[v]||c[u]==c[v]) continue; ok[c[v]].push_back(v); if(mp[c[u]][c[v]]) continue; in[c[v]]++; mp[c[u]][c[v]]=1; ng[c[u]].push_back(c[v]); } } } bool book[N]; //Dijkstra求強連通分量內部的最短路 void dijs(int st){ priority_queue<Node> pq; for(int i=1;i<=n;i++) book[i]=0; pq.push(Node{st,dis[st]}); while(!pq.empty()){ Node now = pq.top(); pq.pop(); int u=now.to; if(book[u]) continue; book[u]=1; for(int i=head[u];~i;i=g[i].nex){ int v=g[i].to; if(c[u]!=c[v]) continue; if(dis[v]>dis[u]+g[i].w){ dis[v]=dis[u]+g[i].w; pq.push(Node{v,dis[v]}); } } } } //Topo排序求強連通分量之間的最短路 queue<int> q; void topo(int st){ int len = scc[st].size(); for(int j=0;j<len;j++){//此強連通分量中的所有點 int u=scc[st][j]; for(int i=head[u];~i;i=g[i].nex){//更新其他強連通分量中的點 int v=g[i].to; if(c[u]==c[v]) continue; dis[v]=min(dis[v],dis[u]+g[i].w); } } len=ng[st].size(); for(int i=0;i<len;i++){//Topo排序 int v=ng[st][i]; in[v]--; if(in[v]==0){ int siz=ok[v].size(); for(int j=0;j<siz;j++) q.push(ok[v][j]); } } } int main(void){ scanf("%d%d%d%d",&n,&x,&y,&s); init(); for(int i=1;i<=x;i++){ int u,v,w; scanf("%d%d%d",&u,&v,&w); add(u,v,w); add(v,u,w); } for(int i=1;i<=y;i++){ int u,v,w; scanf("%d%d%d",&u,&v,&w); add(u,v,w); } getng(); dis[s]=0; q.push(s); while(!q.empty()){ int u =q.front(); q.pop(); dijs(u); if(q.empty()||c[q.front()]!=c[u])//此強連通分量都已處理完 topo(c[u]); } for(int i=1;i<=n;i++) if(dis[i]==inf) puts("NO PATH"); else printf("%d\n",dis[i]); return 0; }