bzoj 2200 道路与航线 最短路进阶

2200: [Usaco2011 Jan]道路和航线

Time Limit: 10 Sec   Memory Limit: 259 MB
Submit: 528   Solved: 173
[ Submit][ Status][ Discuss]

Description

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

Input

* 第1行:四个空格隔开的整数: T, R, P, and S * 第2到R+1行:三个空格隔开的整数(表示一条道路):A_i, B_i 和 C_i * 第R+2到R+P+1行:三个空格隔开的整数(表示一条航线):A_i, B_i 和 C_i

Output

* 第1到T行:从S到达城镇i的最小花费,如果不存在输出"NO PATH"。

Sample Input


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

样例输入解释:

一共六个城镇。在1-2,3-4,5-6之间有道路,花费分别是5,5,10。同时有三条航线:3->5,
4->6和1->3,花费分别是-100,-100,-10。FJ的中心城镇在城镇4。


Sample Output


NO PATH
NO PATH
5
0
-95
-100

样例输出解释:

FJ的奶牛从4号城镇开始,可以通过道路到达3号城镇。然后他们会通过航线达到5和6号城镇。
但是不可能到达1和2号城镇。

wa了无数遍,最终发现bfs算入度的时候没标记起点,啊啊~~错的萌萌哒~~

题意:在一个含有向边(存在负边),无向边的图中,给你一个起点,求出起点到所有点的最短路,

有负边,spfa搞。。超时,堆优化的dijkstra不能处理负边~~。

思路:由于题目说了,如果有A到B的航线,不会有B到A的航线或道路。就是说不会存在含有 有向边的环。所以我们把所有的无向边的点缩点,缩点后会发现此图变成一个有向无环图,对于有向无环图我们可以top序dp求出起点到每个连通块的最短路。然后在每个连通块中用dijkstra求最短路,就行了。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<queue>
#include<cmath>
#include<stack>
#include<set>
#include<map>
#define INF 0x3f3f3f3f
#define MAX 25005
#define mod 10000007
#define CLR(a,b) memset((a),(b),sizeof((a)))
#pragma comment(linker, "/STACK:102400000,102400000")
#define ul u<<1
#define ur (u<<1)1
using namespace std;
typedef long long ll;
struct edge {
    int v,w;
    edge(int _v,int _w) : v(_v),w(_w) {}
};
struct node {
    int u,d;
    node (int _u,int _d): u(_u),d(_d) {}
    bool operator <(const node & a) const {
        return a.d<d;
    }
};
vector<edge> plan[MAX*4],road[MAX*4];
int pre[MAX],dis[MAX],vis[MAX],degree[MAX];
int find(int r) {
    return pre[r] == r ? r : (pre[r] = find(pre[r]));
}
void ol(int n) {
    for(int i=1; i<=n; i++) pre[i]=i;
    for(int i=1; i<=n; i++) {
        int len=road[i].size();
        for(int j=0; j<len; j++) {
            int x=find(i);
            int y=find(road[i][j].v);
            if(x!=y) pre[x]=y;
        }
    }
}
int used[MAX];
void bfs(int s) {
    queue<int> de;
    de.push(s);
    used[s]=1;
    while(!de.empty()) {
        int u=de.front();
        de.pop();
        int lenr=road[u].size();
        for(int i=0; i<lenr; i++) {
            int v=road[u][i].v;
            if(!used[v]) {
                used[v]=1;
                de.push(v);
            }
        }
        int lenp=plan[u].size();
        for(int i=0; i<lenp; i++) {
            int v=plan[u][i].v;
            ++degree[find(v)];
            if(!used[v]) {
                used[v]=1;
                de.push(v);
            }
        }
    }

}
priority_queue<node> q;
vector<int>go[MAX];
queue<int> dp;
void dijkstra(int s) {
    while(!q.empty()) q.pop();
    for(int i=0; i<go[s].size(); i++) {
        int v=go[s][i];
        q.push(node(v,dis[v]));
    }
    while(!q.empty()) {
        node tmp=q.top();
        q.pop();
        int u=tmp.u;
        if(vis[u]) continue;
        vis[u]=1;
        int lenr=road[u].size();
        for(int i=0; i<lenr; i++) {
            int v=road[u][i].v;
            int w=road[u][i].w;
            if(dis[v]>dis[u]+w) {
                dis[v]=dis[u]+w;
                q.push(node(v,dis[v]));
            }
        }
        int len=plan[u].size();
        for(int i=0; i<len; i++) {
            int v=plan[u][i].v;
            int w=plan[u][i].w;
            int root=find(v);
            if(dis[v]>dis[u]+w) {
                dis[v]=dis[u]+w;
                go[root].push_back(v);
            }
            --degree[root];
            if(degree[root]==0) dp.push(root);
        }
    }
}
void top_dp(int s) {
    while(!dp.empty()) dp.pop();
    dp.push(find(s));
    go[find(s)].push_back(s);
    while(!dp.empty()) {
        int now=dp.front();
        dp.pop();
        dijkstra(now);
    }
}
void init() {
    CLR(degree,0);
    CLR(vis,0);
    CLR(used,0);
}
int main() {
    int n,r,p,s,u,v,w;
    init();
    scanf("%d%d%d%d",&n,&r,&p,&s);
    CLR(dis,0x3f);
    dis[s]=0;
    for(int i=1; i<=r; i++) {
        scanf("%d%d%d",&u,&v,&w);
        road[u].push_back(edge(v,w));
        road[v].push_back(edge(u,w));
    }
    ol(n);
    for(int i=1; i<=p; i++) {
        scanf("%d%d%d",&u,&v,&w);
        plan[u].push_back(edge(v,w));
    }
    bfs(s);
    top_dp(s);
    for(int i=1; i<=n; i++) {
        if(dis[i]==INF) printf("NO PATH\n");
        else printf("%d\n",dis[i]);
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值