思路:
天真的想法是把每条边上都建上 \(w-1\) 个点然后跑最短路
但是边权总和很大,显然不能这么做。
考虑对原图求单源最短路,记 \(s\) 到 \(u\) 的最短路为 \(d_u\),可以直接统计在点上的答案。
考虑如何统计在边上的答案,显然每条边至多有 \(2\) 个。
记这个边的两端是 \(u\),\(v\),那么分类讨论从 \(u\) 走过去还是从 \(v\) 走过去,假设从 \(u\) 走,显然需要往边里走 \(l-d_u\),那么这个位置走到 \(v\) 再走到 \(s\) 的距离是 \(w-l+d_u+d_v\),判断是否大于 \(l\) 即可。
注意考虑边上只有一个点的情况,也就是往那边走都是 \(l\)。
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
struct node
{
int to,v;
bool operator<(const node &o) const
{
return v>o.v;
}
};
int x[(int)(1e5+10)],y[(int)(1e5+10)],w[(int)(1e5+10)];
int dis[(int)(1e5+10)],n,s,m;
vector<node>edge[(int)(1e5+10)];
priority_queue<node>q;
bool v[(int)(1e5+10)];
signed main()
{
cin>>n>>m>>s;
for(int i=1;i<=n;i++) dis[i]=INT_MAX;
for(int i=1;i<=m;i++)
{
int u,v,d;cin>>u>>v>>d;
x[i]=u,y[i]=v,w[i]=d;
edge[u].push_back((node){v,d});
edge[v].push_back((node){u,d});
}
dis[s]=0;
q.push((node){s,0});
while(q.size())
{
int u=q.top().to;
q.pop();
if(v[u]) continue;
v[u]=1;
for(int i=0;i<edge[u].size();i++)
{
if(dis[edge[u][i].to]>dis[u]+edge[u][i].v)
{
dis[edge[u][i].to]=dis[u]+edge[u][i].v;
q.push((node){edge[u][i].to,dis[edge[u][i].to]});
}
}
}
int cnt=0,l;
cin>>l;
for(int i=1;i<=n;i++)
{
cnt+=(dis[i]==l);
}
for(int i=1;i<=m;i++)
{
if(dis[x[i]]+w[i]-l+dis[y[i]]==l&&dis[y[i]]<l&&dis[x[i]]<l)
{
// cout<<"case 1: "<<i<<"\n";
cnt++;
continue;
}
if(dis[x[i]]+w[i]-l+dis[y[i]]>l&&dis[x[i]]<l)
{
// cout<<"case 2: "<<i<<"\n";
cnt++;
}
if(dis[x[i]]+w[i]-l+dis[y[i]]>l&&dis[y[i]]<l)
{
// cout<<"case 3: "<<i<<"\n";
cnt++;
}
}
cout<<cnt<<"\n";
}