道路与航线 题解
农夫约翰正在一个新的销售区域对他的牛奶销售方案进行调查。
他想把牛奶送到T个城镇,编号为1~T。
这些城镇之间通过R条道路 (编号为1到R) 和P条航线 (编号为1到P) 连接。
每条道路i或者航线i连接城镇Ai到Bi,花费为Ci。
对于道路,0≤Ci≤10,000;然而航线的花费很神奇,花费CiCi可能是负数(−10,000≤Ci≤10,000)。
道路是双向的,可以从Ai到Bi,也可以从BiBi到Ai,花费都是Ci。
然而航线与之不同,只可以从Ai到Bi。
事实上,由于最近恐怖主义太嚣张,为了社会和谐,出台了一些政策保证:如果有一条航线可以从Ai到Bi,那么保证不可能通过一些道路和航线从Bi回到Ai。
由于约翰的奶牛世界公认十分给力,他需要运送奶牛到每一个城镇。
他想找到从发送中心城镇S把奶牛送到每个城镇的最便宜的方案。
输入格式
第一行包含四个整数T,R,P,S。
接下来R行,每行包含三个整数(表示一个道路)Ai,Bi,Ci。
接下来P行,每行包含三个整数(表示一条航线)Ai,Bi,Ci。
输出格式
第1..T行:第i行输出从S到达城镇i的最小花费,如果不存在,则输出“NO PATH”。
数据范围
1≤T≤250001≤T≤25000,
1≤R,P≤500001≤R,P≤50000,
1≤Ai,Bi,S≤T1≤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\n"); else printf("%d\n", dis[i]); } return 0; }