1035 道路和航线 整体负权且连通块内正权卡spfa求最短路 dijkstra+拓扑序+并查集或者dfs求连通块

 链接:https://ac.nowcoder.com/acm/contest/26077/1035
来源:牛客网

题目描述

FarmerJohn正在一个新的销售区域对他的牛奶销售方案进行调查。他想把牛奶送到T个城镇,编号为1到T。这些城镇之间通过R条道路(编号为1到R)和P条航线(编号为1到P)连接。每条道路i或者航线i连接城镇AiA_iAiBiB_iBi,花费为CiC_iCi
对于道路,0≤Ci≤1040 \le C_i \le 10^40Ci104,然而航线的花费很神奇,花费CiC_iCi可能是负数。道路是双向的,可以从AiA_iAiBiB_iBi,也可以从BiB_iBiAiA_iAi,花费都是CiC_iCi。然而航线与之不同,只可以从AiA_iAiBiB_iBi
事实上,由于最近恐怖主义太嚣张,为了社会和谐,出台了一些政策保证:如果有一条航线可以从AiA_iAiBiB_iBi,那么保证不可能通过一些道路和航线从BiB_iBi回到AiA_iAi。由于FJ的奶牛世界公认十分给力,他需要运送奶牛到每一个城镇。他想找到从发送中心城镇S把奶牛送到每个城镇的最便宜的方案,或者知道这是不可能的。

输入描述:

第一行为四个空格隔开的整数:T,R,P,S;
第二到第R+1行:三个空格隔开的整数(表示一条道路):Ai,BiA_i,B_iAi,BiCiC_iCi
第R+2到R+P+1行:三个空格隔开的整数(表示一条航线):Ai,BiA_i,B_iAi,BiCiC_iCi

输出描述:

输出T行,第i行表示到达城镇i的最小花费,如果不存在输出NO PATH。
示例1

输入

复制
6 3 3 4 
1 2 5 
3 4 5 
5 6 10 
3 5 -100 
4 6 -100 
1 3 -10 

输出

复制
NO PATH
NO PATH
5
0
-95
-100

说明

一共六个城镇。在1和2,3和4,5和6之间有道路,花费分别是5,5,10。同时有三条航线:3→53 \to535,4→64 \to646和1→31 \to313,花费分别是-100,-100,-10。FJ的中心城镇在城镇4。FJ的奶牛从4号城镇开始,可以通过道路到达3号城镇。然后他们会通过航线达到5和6号城镇。但是不可能到达1和2号城镇。

备注:

对于全部数据,1≤T≤2.5×104,1≤R,P≤5×104,1≤Ai,Bi,S≤T1 \le T \le 2.5 \times 10^4,1 \le R,P \le 5 \times 10^4,1 \le A_i,B_i,S \le T1T2.5×104,1R,P5×104,1Ai,Bi,ST。保证对于所有道路,0≤Ci≤1040 \le C_i \le 10^40Ci104,对于所有航线,−104≤Ci≤104-10^4 \le C_i \le 10^4104Ci104。

分析

题意:道路是双向且都是正权的路,航道是单向且有正权也有负权,求从 s 点到每个点的最短距离,如果到不了就输出NO PATH

很容易想到spfa求单源最短路,但是队列优化复杂度是(n+m)logm 求容易退化到nm。(被卡)

题目说航道,如果能从 a 到 b 就不存在其它路径从b 到 a,所以考虑对航道进行拓扑序的求最短路。

由于航道的两个节点a,b 都是不能逆向到达,可以想到a 都在一个道路块里, b 在另一个道路块里

这个道路块由于都是正向边是可以单独求dijkstra的。

对于航道求拓扑。可以先将所有入度为 0 的被标记为使用的连通块放入队列,然后用 dijkstra 逐步更新每个连通块的入度

如何分块?

使用并查集,将每个道路的两个端点放到一个集合里。再遍历每个点,将它们放到编号为它们根节点的连通块里,block[find(i)].pb(i)。并给每个被用作集合的连通块的节点标号

如何构建出度?

在输入航道的时候,把出点所在集合的入度++

dijkstra如何区分子节点是属于当前连通块还是另一个连通块?

if(find(u) == find(v)) 说明在一个连通块,重新放回dijkstra数组 去求最短路

if(find(u) != find(v)) 说明不是一个连通块,将 v 的连通块的入度减少 dd[find(v)] -- ;

//-------------------------代码----------------------------

#define int ll
const int N = 25005;
int n,m,t,tr,r,s;
V<pii> e[N];
int dist[N];
bool vis[N];
int p[N];
V<int> block[N];
int dd[N];
queue<int> q;

int find(int x) {
    return x == p[x] ? x : p[x] = find(p[x]);
}
int cnt = 0;
void dij(int x) {
    memset(vis,false,sizeof(vis));
    priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> pq;
    for (int each : block[x]){
        pq.push({dist[each],each});
    }
     while (!pq.empty()){
        auto cur = pq.top(); pq.pop();
        int u = cur.second;
        if (vis[u]) continue;
        vis[u] = true;
        for (auto each : e[u]){
            int v = each.first;
            int w = each.second;
            if(dist[v] > dist[u] + w) {
                dist[v] = dist[u] + w;
                if(find(v) == find(u)) pq.push({dist[v],v});
            }
            if(find(v) != find(u) && (-- dd[find(v)] == 0)) q.push(find(v));
        }
    }
}

void topo() {
    memset(dist,INF,sizeof dist);
    dist[s] = 0;
    for(int i = 1;i<=t;i++) {
        if(dd[i] == 0) {
            q.push(i);
        }
    }
    while(q.size()) {
        auto u = q.front();q.pop();
        dij(u);
    }
}

void solve()
{
    ms(dd,-1);
    cin>>t>>tr>>r>>s;
    for(int i = 1;i<=t;i++) { p[i] = i; }
    for(int i = 1;i<=tr;i++) {
        int a,b,c;cin>>a>>b>>c;
        e[a].pb({b,c});
        e[b].pb({a,c});
        if(find(a) != find(b))p[find(a)] = find(b);
    }
    for(int i = 1;i<=t;i++) {
        block[find(i)].pb(i);
        dd[find(i)] = 0;
    }
    for(int i = 1;i<=r;i++) {
        int a,b,c;cin>>a>>b>>c;
        e[a].pb({b,c});
        dd[find(b)] ++ ;
    }
    topo();
    for(int i = 1;i<=t;i++) {
        if(dist[i] > INF / 2) cout<<"NO PATH"<<endl;
        else cout<<dist[i]<<endl;
    }
}
void main_init() {}
signed main(){
    AC();clapping();TLE;
    cout<<fixed<<setprecision(12);
    main_init();
//  while(cin>>n,n)
//  while(cin>>n>>m,n,m)
//    int t;cin>>t;while(t -- )
    solve();
//    {solve(); }
    return 0;
}

/*样例区


*/

//------------------------------------------------------------

 

posted @ 2022-08-24 00:03  er007  阅读(17)  评论(0编辑  收藏  举报