原题:https://ac.nowcoder.com/acm/problem/16416
题意:设d为1-n的最短路,求从1-n长度为[d,d+k]的路径的个数。若为无穷种,则输出-1。
题解:不妨先考虑无穷的情况,若最短路中存在0环,就有无穷种。令dis[u]表示u-n的最短路,
f
[
u
]
[
i
]
f[u][i]
f[u][i]表示u->n长度比
d
i
s
[
u
]
dis[u]
dis[u]大i的路径个数。考虑u通过v再到n,则多花费的路程为
t
=
w
[
u
]
[
v
]
+
d
i
s
[
v
]
−
d
i
s
[
u
]
t=w[u][v]+dis[v]-dis[u]
t=w[u][v]+dis[v]−dis[u].有转移方程
f
[
u
]
[
i
]
=
∑
f
[
v
]
[
i
−
t
]
f[u][i]=\sum f[v][i-t]
f[u][i]=∑f[v][i−t]。用记忆化搜素求
f
f
f。dis[u]可以建反图最短路。
如何判断0环记vis[u][i]表示f(u,i)有没有被访问过如果转移方程再dfs中出现两次则证明有0环直接输出-1就行了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
struct edge{
int from,to;ll w;
edge(){}
edge(int a,int b,ll c){
from=a;to=b;w=c;
}
};
struct node{
ll w;int p;
node(){
}
node(ll a,int b){
w=a;p=b;
}
bool operator < (const node & a)const{
return w>a.w;
}
};
vector<edge> v[N][2];//
ll dis[N];
int n,m,cas,k,p;
bool done[N];
bool vis[N][60];
ll f[N][60];
bool flag;
inline void init(){
// memset(dis,0x3f,sizeof dis);
// memset(done,0,sizeof done);
for(int i=1;i<=n;i++){
v[i][0].clear();
v[i][1].clear();
}
memset(f,-1,sizeof f);
memset(vis,0,sizeof vis);
}
priority_queue<node> q;
inline void dijkstra(){//最短路
memset(done,0,sizeof done);
memset(dis,0x3f,sizeof dis);
while(!q.empty()) q.pop();
dis[n]=0;
q.push(node(0,n));
while(!q.empty()){
node u=q.top();q.pop();
if(done[u.p]){
continue;
}
done[u.p]=1;
for(int i=0;i<(int)v[u.p][1].size();i++){
edge y=v[u.p][1][i];
if(done[y.to]) continue;
if(dis[y.to]>y.w+u.w){
dis[y.to]=y.w+u.w;
q.push(node(dis[y.to],y.to));
}
}
}
}
ll dfs(int u,int now){
if(vis[u][now]==1 || flag==1){//0环
flag=1;
return 0;
}
if(f[u][now] != -1){//记忆化搜索
vis[u][now]=0;
return f[u][now];
}
ll &tmp=f[u][now];
vis[u][now]=1;//开始转移
tmp=0;
for(int i=0;i<v[u][0].size();i++){
int y=v[u][0][i].to;
ll t=now-(v[u][0][i].w+dis[y]-dis[u]);
if(t>=0){
tmp=(tmp+dfs(y,t))%p;
}
if(flag) return 0;
}
vis[u][now]=0;//退出
if(u==n && now==0) {//终止条件
tmp=1;
// printf("%d %d\n",u,now);
// printf("%lld %lld\n",tmp,f[u][now]);
}
return tmp;
}
int main(){
scanf("%d",&cas);
while(cas--){
scanf("%d%d%d%d",&n,&m,&k,&p);
init();
for(int i=1;i<=m;i++){
ll w;int x,y;
scanf("%d%d%lld",&x,&y,&w);
v[x][0].push_back(edge(x,y,w));
v[y][1].push_back(edge(y,x,w));//建反图
}
dijkstra();
ll ans=0;flag=0;
for(int i=0;i<=k;i++){
ans=(ans+dfs(1,i))%p;
if(flag) break;
}
if(flag) printf("-1\n");
else printf("%lld\n",ans);
}
return 0;
}