2019ICPC(銀川) – Delivery Route(強連通分量 + 拓撲排序 + dijkstra)

Delivery Route

題目:有n個派送點,x條雙向邊,y條單向邊,出發點是s,雙向邊的權值均為正,單向邊的權值可以為負數,對於單向邊給出了一個限制:如果u->v成立,則v->u一定不成立。問,從s出發,到其他所有點的最短路是多少(包括s)。

思路:對於單向邊的限制,我們可以這麼理解:雙向邊相連接的點一定組成一個強連通分量,如果一條單向邊存在於某個強連通分量中,可以得出:如果“u -> v”,則一定“v -> u”,可以推出單向邊一定只存在於連接兩個強連通分量,且還可以推出,強連通分量縮點後,連上單項邊,此時的圖一定是一個有向無環圖,於是給出的限制”對於單向邊給出了一個限制:如果u->v成立,則v->u一定不成立。”完全成立,於是圖的性質我們分析完了。

①似乎這個圖的性質可以直接跑dijkstra,的確可以,但是負權邊的存在複雜度太大。

②每個強連通分量都可以dijkstra,且圖存在拓撲排序,不如讓入度為0的縮點先跑dijkstra,然後一條單向邊隻影響其他強連通分量的一個點的距離,然後按照拓撲序來確定每個強連通分量跑dijkstra的順序。

 

  1 #include <iostream>    2 #include <cstdio>    3 #include <algorithm>    4 #include <queue>    5    6 #define ll long long    7 #define pb push_back    8 #define fi first    9 #define se second   10   11 using namespace std;   12   13 const int N = 25000 + 10;   14 const int M = 50000 + 10;   15 const ll INF = 1e10;   16 struct Edge{   17     int to, nxt, w;   18 }e[M << 2];   19 struct node{   20     int u, v, w;   21 };   22 struct tmp{   23     int now;   24     ll w;   25     bool friend operator<(const tmp& a, const tmp& b){   26         return a.w > b.w;   27     }   28 };   29 int head[N], scc[N], du[N], vis[N], ok[N];   30 ll dis[N];   31 vector<node > vp[N];//單向邊   32 vector<int > belong[N];//屬於哪個scc   33 vector<int > mp[N];//存邊   34 priority_queue<tmp > pque;   35 int n, x, y, s, tot, col;   36   37 inline void add(int u, int v, int w){   38     e[tot].to = v; e[tot].nxt = head[u];   39     e[tot].w = w; head[u] = tot++;   40 }   41   42 //縮點   43 void dfs(int now){   44     scc[now] = col;   45     belong[col].pb(now);   46     for(int o = head[now]; ~o; o = e[o].nxt)   47         if(!scc[e[o].to]) dfs(e[o].to);   48 }   49   50 //檢測這個點是不是有效點   51 void check(int now){   52     ok[now] = 1;   53     for(auto to : mp[now])   54         if(!ok[to]) check(to);   55 }   56   57 void dijkstra(int ss){   58     while(!pque.empty()) pque.pop();   59     if(ss == s) pque.push({ss, dis[ss]}); //圖一定是從出發點s開始的   60     else{   61         //相當於從一個超級源點出發   62         for(auto it : belong[scc[ss]]) pque.push({it, dis[it]});   63     }   64     while(!pque.empty()){   65         int u = pque.top().now;   66         pque.pop();   67         if(vis[u]) continue;   68         vis[u] = 1;   69         for(int o = head[u]; ~o; o = e[o].nxt){   70             if(dis[u] + e[o].w < dis[e[o].to]){   71                 dis[e[o].to] = dis[u] + e[o].w;   72                 pque.push({e[o].to, dis[e[o].to]});   73             }   74         }   75     }   76 }   77   78 void top_sort(){   79     queue<int > que;   80     que.push(scc[s]);//滿足的圖 應該是從s的連通圖出發的拓撲圖   81     dijkstra(s);   82     while(!que.empty()){   83         int inx = que.front();   84         que.pop();   85         for(auto it : vp[inx]){   86             //一條單向邊影響一個點的距離   87             if(dis[it.u] + it.w < dis[it.v]){   88                 dis[it.v] = dis[it.u] + it.w;   89             }   90             //入度0,跑dijkstra   91             if(--du[scc[it.v]] == 0){   92                 que.push(scc[it.v]);   93                 dijkstra(it.v);   94             }   95         }   96     }   97 }   98   99  100 void solve(){  101     scanf("%d%d%d%d", &n, &x, &y, &s);  102     for(int i = 1; i <= n; ++i) head[i] = -1; tot = 0;  103     for(int i = 1; i <= n; ++i) dis[i] = INF; dis[s] = 0;  104     int u, v, w;  105     for(int i = 1; i <= x; ++i){  106         scanf("%d%d%d", &u, &v, &w);  107         add(u, v, w); add(v, u, w);  108         mp[u].pb(v); mp[v].pb(u);  109     }  110  111     vector<node > tmp;  112     for(int i = 1; i <= y; ++i){  113         scanf("%d%d%d", &u, &v, &w);  114         tmp.pb({u, v, w});  115         mp[u].pb(v);  116     }  117     //圖一定是從出發點s開始的,所以從s出發遍歷圖,無法到達的點,就是無法到達的點  118     //檢測這個點是不是有效點  119     check(s);  120     //縮點  121     for(int i = 1; i <= n; ++i){  122         if(!scc[i] && ok[i]){  123             ++col;  124             dfs(i);  125         }  126     }  127     //入度統計  128     for(auto x : tmp){  129         if(ok[x.u] && ok[x.v]){//有效點  130             vp[scc[x.u]].pb(x);  131             ++du[scc[x.v]];  132         }  133     }  134     top_sort();//拓撲序  135     for(int i = 1; i <= n; ++i){  136         if(dis[i] == INF) printf("NO PATHn");  137         else printf("%lldn", dis[i]);  138     }  139 }  140  141 int main(){  142  143     solve();  144  145     return 0;  146 }  147  148 /*  149 7 5 3 4  150 1 2 5  151 3 4 5  152 5 6 10  153 5 7 4  154 6 7 105  155 3 5 -100  156 4 6 -100  157 7 2 -100  158 */