洛谷P3008 [USACO11JAN]Roads and Planes G
题目链接:P3008 [USACO11JAN]Roads and Planes G
题目大意:
给定一张图,有 \(T\) 个点,\(R\) 条双向边和 \(P\) 条单向边,保证每条单向边 \(u\rightarrow v\) 不存在道路使得 \(v\rightarrow u\) ,其中单向边的长度可能为负数,给定起点 \(S\) 求单源最短路。
\(1\leq T\leq 25000\) ,\(1\leq R,P\leq 50000\)
思路:
图中有负权边,据我所知出题人卡了SPFA,所以不要想了。
注意到只有单向边有负权,把它们拉出来,当图中只剩下双向边时,会产生若干个连通块,把单向边加回去时,这些块重新连起来,考虑将其缩点,那么单向边组成的
就变成了一张DAG,DAG上单源最短路可以直接拓扑,不受负权影响,这启发我们建DAG,点内跑Dijkstra,图上跑拓扑。时间复杂度 \(O(T+P+RlogT)\)
细节
- 由于有负边权,dis数组上赋的极大值可能会被削小,判"NO PATH"的时候不能直接"==0x3f3f3f3f"。
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#define N 25010
#define E 50010
#define PII pair<int,int>
#define fr first
#define sc second
#define mk make_pair
using namespace std;
int head[N],nxt[E*3],to[E*3];
int cnt,val[E*3],conn[N];
vector<int> tot[N];
int deg[N],dis[N];
bool vis[N];
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
return s*w;
}
void init(){
memset(head,-1,sizeof(head));
memset(dis,0x3f,sizeof(dis));
cnt=-1;
}
void add_e(int a,int b,int c,bool id){
nxt[++cnt]=head[a];
head[a]=cnt;
to[cnt]=b;
val[cnt]=c;
if(id)add_e(b,a,c,0);
}
void dfs(int x,int id){
vis[x]=true;
conn[x]=id;
tot[id].push_back(x);
for(int i=head[x];~i;i=nxt[i]){
if(vis[to[i]])continue;
dfs(to[i],id);
}
}
queue<int> topo;
priority_queue<PII,vector<PII>,greater<PII> > q;
bool Min(int &a,int b){
if(a<=b)return false;
a=b;return true;
}
void dijkstra(int x){
for(int i=0;i<tot[x].size();i++){
q.push(mk(dis[tot[x][i]],tot[x][i]));
}
while(!q.empty()){
int cur=q.top().sc; q.pop();
if(vis[cur])continue;
vis[cur]=true;
for(int i=head[cur];~i;i=nxt[i]){
int v=to[i];
if(Min(dis[v],dis[cur]+val[i]))
if(conn[cur]==conn[v])q.push(mk(dis[v],v));
if(conn[cur]!=conn[v]&&--deg[conn[v]]==0)topo.push(conn[v]);
}
}
}
int t,r,p,s;
int main(){
cin>>t>>r>>p>>s;
int a,b,c;
init();
for(int i=0;i<r;i++){
a=read(),b=read(),c=read();
add_e(a,b,c,1);
}
for(int i=1;i<=t;i++) if(!vis[i])dfs(i,i);
for(int i=0;i<p;i++){
a=read(),b=read(),c=read();
deg[conn[b]]++;
add_e(a,b,c,0);
}
memset(vis,false,sizeof(vis));
dis[s]=0;
for(int i=1;i<=t;i++)
if(conn[i]==i&&!deg[i])topo.push(i);
while(!topo.empty()){
int now=topo.front(); topo.pop();
dijkstra(now);
}
for(int i=1;i<=t;i++){
if(dis[i]>500000000)puts("NO PATH");
else printf("%d\n",dis[i]);
}
return 0;
}