道路与航线 题解

题目传送门

农夫约翰正在一个新的销售区域对他的牛奶销售方案进行调查。

他想把牛奶送到T个城镇,编号为1~T。

这些城镇之间通过R条道路 (编号为1到R) 和P条航线 (编号为1到P) 连接。

每条道路i或者航线i连接城镇AiBi,花费为Ci

对于道路,0≤Ci≤10,000;然而航线的花费很神奇,花费CiCi可能是负数(10,000Ci10,000)。

道路是双向的,可以从AiBi,也可以从BiBi到Ai,花费都是Ci

然而航线与之不同,只可以从AiBi

事实上,由于最近恐怖主义太嚣张,为了社会和谐,出台了一些政策保证:如果有一条航线可以从AiBi,那么保证不可能通过一些道路和航线从Bi回到Ai

由于约翰的奶牛世界公认十分给力,他需要运送奶牛到每一个城镇。

他想找到从发送中心城镇S把奶牛送到每个城镇的最便宜的方案。

输入格式

第一行包含四个整数T,R,P,S。

接下来R行,每行包含三个整数(表示一个道路)Ai,Bi,Ci

接下来P行,每行包含三个整数(表示一条航线)Ai,Bi,Ci

输出格式

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

数据范围

1T250001≤T≤25000,
1R,P500001≤R,P≤50000,
1Ai,Bi,ST1≤Ai,Bi,S≤T,

数据有负边权,直接用dij是肯定不行的了,我们用spfa,这道题的数据经过特殊构造,会卡掉;

所以我们注意到题目中道路是正边权,只有航线可能出现负数,并且单向边不会构成一个环;

所以我们不妨对道路求出每一个联通块,将其当做一个点,再加入单向边的航线,得到一张有向无环图,可以进行一个拓扑序,线性时间内求出单源最短路;

具体操作:

1.先只加入双向边,然后dfs求出每一个联通块,记为c【x】;

2.统计每个联通块的入度deg【i】,将入度为0的加入队列q,当然我们加入c【S】;

3.设d【s】=0,其余memset正无穷;

4.建立一个堆,将联通块的节点加入,遍历他的所有点找dij,对于当前指向的边y,判断:

if(c【x】!=c【y】) deg【c【y】】--,为零则加入;

if(c【x】== c【y】) 更新y;

拓扑直至队列为空:

#include<bits/stdc++.h>
using namespace std;
#define N 500100
template<typename T>inline void read(T &x) {
    x=0;T f=1,ch=getchar();
    while(!isdigit(ch))  {if(ch=='-')  f=-1;  ch=getchar();}
    while(isdigit(ch))  {x=x*10+ch-'0';  ch=getchar();}
    x*=f;
}
typedef pair <int,int> pii;
priority_queue <pii,vector<pii>,greater<pii> > q1;
queue<int> q;
int T, P, R, S, tot, x, y, z;
int dis[N], vis[N], lin[N], c[N], deg[N];
vector <int> cir[N]; 
struct gg{
    int y,next,v;
}a[N<<1];

inline void add(int x, int y, int z) {
    a[++tot].y = y;
    a[tot].v = z;
    a[tot].next = lin[x];
    lin[x] = tot;
}

void dfs(int now,int na){
    c[now] = na;
    cir[na].push_back(now);
    for (int i = lin[now]; i; i = a[i].next){
        int y = a[i].y;
        if (!c[y]) dfs(y,na);
    }
}

int main() {
    //freopen("1.in","r",stdin);
    read(T); read(R); read(P); read(S);
    for (int i = 1; i <= R; i++) {
        read(x); read(y); read(z);
        add(x, y, z);
        add(y, x, z);
    }
    int num = 0;
    for (int i = 1; i <= T; i++) {
        if(!c[i]) dfs(i, ++num);
    }
    for (int i = 1; i <= P; i++) {
        read(x); read(y); read(z); 
        add(x, y, z);
        ++deg[c[y]];
    }
    q.push(c[S]);
    for (int i = 1; i <= num; i++) {
        if (!deg[i]) q.push(i);
    }
    memset(dis, 0x7f, sizeof(dis));
    dis[S] = 0;
    while (!q.empty()) {
        int now = q.front(); q.pop();
        for (int i = 0; i < cir[now].size(); i++) {
            q1.push(make_pair(dis[cir[now][i]],cir[now][i]));
        }
        while(!q1.empty()) {
            int x = q1.top().second; q1.pop();
            if (vis[x]) continue;
            vis[x] = 1;
            for (int i = lin[x]; i; i = a[i].next) {
                int y = a[i].y;
                if (dis[y] > dis[x] + a[i].v) {
                    dis[y] = dis[x] + a[i].v;
                    if (c[x] == c[y]) q1.push(make_pair(dis[y], y));
                }
                if (c[x] != c[y]) {
                    deg[c[y]]--;
                    if (!deg[c[y]]) q.push(c[y]);
                 }
            }
        }    
    }
    for (int i = 1; i <= T; i++) {
        if (dis[i] > 0x3f3f3f3f) printf("NO PATH
");
        else printf("%d
", dis[i]);
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/Tyouchie/p/10860576.html