很经典(😏)的一道题,发现k不是很大,可以枚举k,然后跑一个记忆化搜索统计答案
思路简单,但是写起来有些难度
可以dp来做,用f[i][j]表示从n到i的距离<最短距离+k的方案数,对于每一条边都尝试走,而这个状态可以表示为u->v: d[u]-d[v]+k-len[u->v];最后西格玛一下就over了
挺神奇的哈
#include<bits/stdc++.h>
using namespace std;
vector<pair<int,int> > v1[1100000],v2[1100000];
inline int read(){
int x=0;
char c=getchar();
bool flag=0;
while(c<'0'||c>'9'){
if(c=='-')
flag=1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=(x<<3)+(x<<1)+c-'0';
c=getchar();
}
return flag?-x:x;
}
int dis[1100000],n,m;
void spfa(int s){
for(int i=1;i<=n;i++){
dis[i]=2147483646;
}
queue<int> q;
dis[s]=0;
q.push(s);
while(!q.empty()){
int now=q.front();
q.pop();
for(int i=0;i<v1[now].size();i++){
int v=v1[now][i].first,w=v1[now][i].second;
if(dis[v]>dis[now]+w){
dis[v]=dis[now]+w;
q.push(v);
}
}
}
}
int k,dp[100100][55],mod;
bool vis[100100][55];
int dfs(int x,int left){
if(left<0||left>k)return 0;
int ans=0;
if(vis[x][left]){
vis[x][left]=0;
return -1;
}
if(dp[x][left]!=-1)return dp[x][left];
vis[x][left]=1;
for(int i=0;i<v2[x].size();i++){
int sum=dfs(v2[x][i].first,dis[x]+left-dis[v2[x][i].first]-v2[x][i].second);
if(sum==-1){
vis[x][left]=0;
return -1;
}
ans=(ans+sum)%mod;
}
vis[x][left]=0;
if(x==1&&left==0)ans++;
dp[x][left]=ans;
return ans;
}
int main(){
ios::sync_with_stdio(0);
int t;
cin>>t;
while(t--){
//n=read();
//m=read();
//k=read();
//mod=read();
cin>>n>>m>>k>>mod;
for(int i=1;i<=n;i++){
v1[i].clear();
v2[i].clear();
}
for(int i=1;i<=m;i++){
int u,v,w;
// u=read();
// v=read();
// w=read();
cin>>u>>v>>w;
v1[u].push_back(make_pair(v,w));
v2[v].push_back(make_pair(u,w));
}
spfa(1);
memset(dp,-1,sizeof(dp));
int ans=0;
for(int i=0;i<=k;i++){
int tmp=dfs(n,i);
if(tmp==-1){
ans=-1;
break;
}
ans=(ans+tmp)%mod;
}
cout<<ans<<endl;
}
return 0;
}