题目
题解
– 首先要在图上跑一遍最短路是肯定的(spfa或迪杰斯特拉)
接着就是怎么算答案了
我们可以先建一个反图(方便从终点跑回起点)
设f[i][j]:从起点跑到i号节点时,与目前的最短路相差j的长度时的方案数
所以说我们怎么从f[i][j]转移到f[a][b]呢(i,a由一条长为w的路径相连)
可以推出:j-b=d[a]+w-d[i]
即:b=j+d[i]-d[a]-w
因为直接递推不能做
然后我们可以用记搜
从终点往回搜,边界状态是f[1][0]=1
用isv[i][j]判断这种情况是否出现过(判断0环)
还要特判一下1在0环内的情况(因为记搜无法判断这种情况)
小技巧:f初始值记为-1,判断是否遍历过用~可以快很多很多很多很多
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
const int MAXN=100005;
const int MAXM=200005;
int t;
int n,m,k,p;
int head[MAXN],next[MAXM],to[MAXM],w[MAXM],cnt;
int Head[MAXN],Next[MAXM],To[MAXM],W[MAXM],Cnt;
queue<int>q;
int d[MAXN];
bool isv[MAXN];
long long f[MAXN][55];
bool Isv[MAXN][55],flag;
long long ans;
void R(int &n){
char c;for(n=0;(c=getchar())<'0'||c>'9';);
for(;c>='0'&&c<='9';c=getchar())n=n*10+c-48;
}
void add(int u,int v,int l){
cnt++;
next[cnt]=head[u];
to[cnt]=v;
w[cnt]=l;
head[u]=cnt;
}
void add1(int u,int v,int l){
Cnt++;
Next[Cnt]=Head[u];
To[Cnt]=v;
W[Cnt]=l;
Head[u]=Cnt;
}
void spfa(){
memset(d,0x7f,sizeof(d));
memset(isv,0,sizeof(isv));
q.push(1);
d[1]=0;
isv[1]=1;
while(q.size()){
int x=q.front();
q.pop();
isv[x]=0;
for(int i=head[x];i;i=next[i]){
int y=to[i];
if(d[x]+w[i]<d[y]){
d[y]=d[x]+w[i];
if(!isv[y]){
isv[y]=1;
q.push(y);
}
}
}
}
}
void eeeeee(){
vector<int>Q;
Q.push_back(1);
for(int h=0;h<Q.size();h++){
int x=Q[h];
for(int i=head[x];i;i=next[i]){
int y=to[i];
if(y==1)
flag=1;
if(flag)
return ;
if(!w[i])
Q.push_back(y);
}
}
}
int dfs(int x,int k){
if(~f[x][k])
return f[x][k];
f[x][k]=0;
for(int i=Head[x];i;i=Next[i]){
int y=To[i];
int j=d[x]-d[y]+k-W[i];
if(j<0)
continue;
if(Isv[y][j])
flag=1;
else{
Isv[y][j]=1;
f[x][k]=(f[x][k]+dfs(y,j))%p;
Isv[y][j]=0;
}
if(flag)
return 0;
}
return f[x][k];
}
int main(){
R(t);
while(t--){
R(n);
R(m);
R(k);
R(p);
cnt=0;
memset(head,0,sizeof(head));
Cnt=0;
memset(Head,0,sizeof(Head));
for(int i=1;i<=m;i++){
int u,v,l;
R(u);
R(v);
R(l);
add(u,v,l);
add1(v,u,l);
}
spfa();
flag=0;
memset(f,-1,sizeof(f));
f[1][0]=1;
memset(Isv,0,sizeof(Isv));
ans=0;
eeeeee();
for(int i=0;i<=k;i++){
if(flag)
break;
ans=(ans+dfs(n,i))%p;
}
if(flag)
printf("-1\n");
else
printf("%lld\n",ans);
}
return 0;
}