题目描述
策策同学特别喜欢逛公园。公园可以看成一张 N 个点 M 条边构成的有向图,且没有 自环和重边。其中1号点是公园的入口, N 号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花的时间。
策策每天都会去逛公园,他总是从1号点进去,从 N 号点出来。
策策喜欢新鲜的事物,它不希望有两天逛公园的路线完全一样,同时策策还是一个 特别热爱学习的好孩子,它不希望每天在逛公园这件事上花费太多的时间。如果1号点 到 N 号点的最短路长为 d ,那么策策只会喜欢长度不超过 d + K 的路线。
策策同学想知道总共有多少条满足条件的路线,你能帮帮它吗?
为避免输出过大,答案对 P 取模。
如果有无穷多条合法的路线,请输出 -1 。
输入
第一行包含一个整数 T , 代表数据组数。
接下来 T 组数据,对于每组数据: 第一行包含四个整数 N,M,K,P ,每两个整数之间用一个空格隔开。
接下来 M 行,每行三个整数 ai,bi,ci ,代表编号为 ai,bi 的点之间有一条权值为 ci 的有向边,每两个整数之间用一个空格隔开(可能有0边)。
对于 100%的数据, 1≤ P ≤ 109,N ≤ 100000 ,M ≤ 200000 ,1 ≤ ai,bi ≤ N,0 ≤ ci ≤ 1000。
输出
输出文件包含 T 行,每行一个整数代表答案。
样例输入
2 5 7 2 10 1 2 1 2 4 0 4 5 2 2 3 2 3 4 1 3 5 2 1 5 3 2 2 0 10 1 2 0 2 1 0
样例输出
3 -1
题解
这就是DAY1 的dp了。
首先很容易想到先spfa求一遍最短路(实际上并不需要求出最短路)。接下来我们要反向建图,再做一遍spfa求出终点 n 到各点的最短路 d[ i ] ,于是在每个点我们都可以知道它离终点的最短距离,然后当我们从点 i 往下一个点 j 走的时候,如果d[ j ] + dis[ i ][ j ] > d[ i ] ,说明我们走了冤枉路,多走的冤枉路的值是 d[ j ] + dis[ i ][ j ] - d[ i ] 。因为我们不能选择大于最短路+k 的长度的路径,所以我们走的冤枉路的最大值就是 k,于是很容易的就想到了dp。
用 dp[ i ][ j ] 表示在 i 号点还可以允许走 j 的冤枉路的方案数,考虑向下一个点 k 转移,转移方程为: dp[ i ][ j ] = Σ dp[ k ][ j - ( dis[ i ][ k ] + d[ k ] - d[ i ] ) ] 。
什么情况输出“-1”呢?题目中说有 0 边,那么当出现 0 环的时候就有无穷多的情况了,只要判掉这种情况即可。
#include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define ll long long #define mem(a,b) memset(a,b,sizeof(a)) const int maxn=100000+50; const int maxm=200000+50; const int inf=2139062143; int n,m,pp,k,a,b,c,T; int fir[maxn],to[maxm],nex[maxm],wi[maxm],ecnt; int fi[maxn],t[maxm],ne[maxm],w[maxm],e; int d[maxn],q[30000001],dp[maxn][60]; bool p[maxn],v[maxn][60],u[maxn]; template<typename T>void read(T& aa){ char cc; ll ff;aa=0;cc=getchar();ff=1; while((cc<'0'||cc>'9')&&cc!='-') cc=getchar(); if(cc=='-') ff=-1,cc=getchar(); while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar(); aa*=ff; } void add_edge(int u,int v,int ww){ nex[++ecnt]=fir[u];fir[u]=ecnt;to[ecnt]=v;wi[ecnt]=ww; ne[++e]=fi[v];fi[v]=e;t[e]=u;w[e]=ww; } void init(){ mem(fir,0);mem(to,0);mem(nex,0);mem(wi,0);ecnt=0; mem(fi,0);mem(t,0);mem(ne,0);mem(w,0);e=0; mem(u,0);mem(dp,-1);mem(v,0); } void spfa(int x){ int head=0,tail=1; memset(p,false,sizeof(p)); memset(d,127,sizeof(d)); p[x]=true; d[x]=0; q[1]=x; while(head<tail){ head++; int u=q[head]; p[u]=false; for(int e=fi[u];e;e=ne[e]){ int v=t[e]; if(d[u]+w[e]<d[v]){ d[v]=d[u]+w[e]; if(!p[v]){ tail++; p[v]=true; q[tail]=v; } } } } } int dfs(int a,int b){ if(b<0) return 0; else if(v[a][b]) return -inf; else if(dp[a][b]!=-1) return dp[a][b]; else{ v[a][b]=1;int key=0; if(a==n) key++; for(int e=fir[a];e;e=nex[e]){ int vv=to[e]; if(!u[vv]) continue; int ww=dfs(vv,b-(d[vv]+wi[e]-d[a])); if(ww==-inf) return -inf; key=(key+ww)%pp; } dp[a][b]=key; v[a][b]=0; return key; } } int main(){ read(T); while(T--){ init(); read(n),read(m),read(k),read(pp); for(int i=1;i<=m;i++){ read(a),read(b),read(c); add_edge(a,b,c); } spfa(n); for(int i=1;i<=n;i++) if(d[i]!=inf) u[i]=true; int z=dfs(1,k); if(z==-inf) cout<<"-1"<<endl; else cout<<z<<endl; } return 0; }