表示 号点到 号点的最短距离,用 跑一遍即可。
考虑如何设定状态。
注意到题意要的是小于等于 的路径数量,而在我们跑完 之后 就是一个常数,而 的范围就比较和谐 ,所以状态从这里入手。
表示
的路径中,路径长度比
大
的条数,设
是
出边的另外一点,考虑边权转移得到状态转移方程:
由于这样的状态并不好直接写几个 来转移,所以用记忆化搜索,搜索的同时记录一下某一个状态是否重复出现,如果重复出现了那就说明存在 环。
最后的答案就是:
#include <queue>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define DB double
#define SG string
using namespace std;
const int Max=1e5+5;
const int Inf=1e9;
int T,N,M,K,P,Ans,Flag,Vis[Max],Dis[Max],DP[Max][55],Vis_DP[Max][55];
struct Node{
int Cnt,To[Max<<1],Edge[Max<<1],Next[Max<<1],Head[Max];
void Init(){
Cnt=0;
memset(Head,0,sizeof(Head));
}
void Insert(int X,int Y,int Z){
To[++Cnt]=Y;Edge[Cnt]=Z;Next[Cnt]=Head[X];Head[X]=Cnt;
}
void SPFA(int S){
int I,X;
queue<int>Q;
for(I=1;I<=N;I++){
Vis[I]=0;
Dis[I]=Inf;
}Q.push(S),Vis[S]=1,Dis[S]=0;
while(Q.size()){
X=Q.front();
Q.pop();
Vis[X]=0;
for(I=Head[X];I;I=Next[I]){
int Y=To[I],Z=Edge[I];
if(Dis[X]+Z<Dis[Y]){
Dis[Y]=Dis[X]+Z;
if(Vis[Y]==0){
Q.push(Y);
Vis[Y]=1;
}
}
}
}
}
}G1,G2;
inline int Read(){
int X=0;char CH=getchar();bool F=0;
while(CH>'9'||CH<'0'){if(CH=='-')F=1;CH=getchar();}
while(CH>='0'&&CH<='9'){X=(X<<1)+(X<<3)+CH-'0';CH=getchar();}
return F?-X:X;
}
inline void Write(int X){
if(X<0)X=-X,putchar('-');
if(X>9)Write(X/10);
putchar(X%10+48);
}
void Clear(){
Ans=0;Flag=1;
G1.Init();G2.Init();
memset(DP,-1,sizeof(DP));
}
int DFS(int X,int K){
int I,J;
if(~DP[X][K]){
return DP[X][K];
}
Vis_DP[X][K]=1;DP[X][K]=0;
for(I=G2.Head[X];I;I=G2.Next[I]){
int Y=G2.To[I],Z=Dis[X]-Dis[Y]+K-G2.Edge[I];
if(Z>=0){
if(Vis_DP[Y][Z]){
Flag=0;
}
DP[X][K]+=DFS(Y,Z);DP[X][K]%=P;
}
}
Vis_DP[X][K]=0;
return DP[X][K];
}
int main(){
int I,J;
T=Read();
while(T--){
Clear();
N=Read(),M=Read(),K=Read(),P=Read();
for(I=1;I<=M;I++){
int X=Read(),Y=Read(),Z=Read();
G1.Insert(X,Y,Z);G2.Insert(Y,X,Z);
}
G1.SPFA(1);DP[1][0]=1;
for(I=0;I<=K;I++){
Ans+=DFS(N,I);Ans%=P;
}
DFS(N,K+1);
if(!Flag){
puts("-1");
} else {
printf("%lld\n",Ans);
}
}
return 0;
}