https://nanti.jisuanke.com/t/42388
尝试写了一下spfa没加优化跑不过去,据说加了优化能过?
艹这题也巨水去年没时间写了
由于它说了如果存在单向边从u->v,那么一定无法从v->u,而由于只有单向边有负数,所以对于每一个双向边连起来的连通块内部,是不存在单向边的,那么单向边总是从一个连通块连向另一个连通块,于是就可以按照单向边来拓扑排序
先把当前rudu为0的连通块拿出来用双向边跑一遍dij,然后用单向边去更新别的连通块中的点的最小dis,接着拓扑排序一个新的连通块继续如此操作
有个小细节找了一晚上错误,由于单向边u->v是可能<0的,必须要dis[u]<inf也就是可走到的才能取更新dis[v]
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxl=1e5+10;
const int inf=1e9;
int n,xm,ym,st;
int dis[maxl],f[maxl],du[maxl];
struct ed
{
int v,l;
};
vector<ed> ex[maxl],ey[maxl];
vector<int> b[maxl];
bool in[maxl];
queue<int> tq;
typedef pair<int,int> p;
priority_queue<p,vector<p>,greater<p>> q;
inline int find(int x)
{
if(f[x]!=x)
f[x]=find(f[x]);
return f[x];
}
inline void prework()
{
scanf("%d%d%d%d",&n,&xm,&ym,&st);
for(int i=1;i<=n;i++)
f[i]=i;
int u,v,c,x,y;
for(int i=1;i<=xm;i++)
{
scanf("%d%d%d",&u,&v,&c);
ex[u].push_back(ed{v,c});
ex[v].push_back(ed{u,c});
x=find(u);y=find(v);
f[y]=x;
}
for(int i=1;i<=n;i++)
f[i]=find(i);
for(int i=1;i<=ym;i++)
{
scanf("%d%d%d",&u,&v,&c);
ey[u].push_back(ed{v,c});
du[f[v]]++;
}
}
inline void solv(int id)
{
while(q.size()) q.pop();
for(int i:b[id])
if(dis[i]<inf)
q.push({dis[i],i});
int u;p d;
while(!q.empty())
{
d=q.top();q.pop();
u=d.second;
if(d.first!=dis[u])
continue;
for(ed ee:ex[u])
if(dis[ee.v]>dis[u]+ee.l)
{
dis[ee.v]=dis[u]+ee.l;
q.push({dis[ee.v],ee.v});
}
}
for(int u:b[id])
for(ed ee:ey[u])
{
if(dis[ee.v]>dis[u]+ee.l && dis[u]<inf)
dis[ee.v]=dis[u]+ee.l;
du[f[ee.v]]--;
if(!du[f[ee.v]])
tq.push(f[ee.v]);
}
}
inline void mainwork()
{
for(int i=1;i<=n;i++)
if(f[i]==i)
{
if(du[i]==0)
tq.push(i);
}
for(int i=1;i<=n;i++)
dis[i]=inf,b[f[i]].push_back(i);
dis[st]=0;
while(!tq.empty())
{
int u=tq.front();
tq.pop();
solv(u);
}
}
inline void print()
{
for(int i=1;i<=n;i++)
if(dis[i]==inf)
puts("NO PATH");
else
printf("%d\n",dis[i]);
}
int main()
{
prework();
mainwork();
print();
return 0;
}