题目:luogu3953.
题目大意:给定一张
n
n
n个点
m
m
m条边的非负整数权图,求解这张图中比
1
1
1到
n
n
n最短路长不超过
k
k
k的
1
1
1到
n
n
n的路径条数,若无解输出
−
1
-1
−1.
1
≤
n
≤
1
0
5
,
1
≤
m
≤
2
∗
1
0
5
,
1
≤
k
≤
50
1\leq n\leq 10^5,1\leq m\leq 2*10^5,1\leq k\leq 50
1≤n≤105,1≤m≤2∗105,1≤k≤50.
很显然这种最短路计数的题,当然是要先跑一遍最短路了,设点 1 1 1到点 i i i的最短路为 d i s [ i ] dis[i] dis[i].
考虑70pts的部分分,也就是先不考虑无穷条路径的情况.那可以直接设 d p [ i ] [ j ] dp[i][j] dp[i][j]表示所有从 1 1 1到 i i i的路径中,比 1 1 1到 i i i的最短路长 j j j的路径条数.转移时若有边 ( x , y , v ) (x,y,v) (x,y,v),则状态 d p [ y ] [ i ] dp[y][i] dp[y][i]就可以通过 d p [ x ] [ i + d i s [ y ] − v − d i s [ x ] ] dp[x][i+dis[y]-v-dis[x]] dp[x][i+dis[y]−v−dis[x]]转移,记忆化搜索实现即可.时间复杂度 O ( ( n + m ) k ) O((n+m)k) O((n+m)k).
然后是无穷条路径的情况.无穷条路径的情况需要满足两个条件,一个是有 0 0 0环,一个是有一条长度不超过最短路长度 + k +k +k的路径经过 0 0 0环,问题变为了如何判断这两个条件是否满足.
先用拓扑排序把所有 0 0 0环上的点找出来,判断是否有 0 0 0环上的点被一条满足条件的路径经过,判断某个点 i i i是否被经过只需要判断 1 1 1到 i i i的最短路加上 i i i到 n n n的最短路是否小于等于 1 1 1到 n n n的最短路 + k +k +k即可.
总时间复杂度 O ( ( n + m ) k ) O((n+m)k) O((n+m)k).
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=100000,M=200000,K=50,INF=(1<<30)-1;
int n,m,sk,mod;
int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
int sub(int a,int b){return a-b<0?a-b+mod:a-b;}
int mul(int a,int b){return (LL)a*b%mod;}
void sadd(int &a,int b){a=add(a,b);}
void ssub(int &a,int b){a=sub(a,b);}
void smul(int &a,int b){a=mul(a,b);}
struct side{
int y,next,v;
}e[M*2+9];
int lin[2][N+9],cs;
void Ins(int id,int x,int y,int v){e[++cs].y=y;e[cs].v=v;e[cs].next=lin[id][x];lin[id][x]=cs;}
struct state{
int x,v;
state(int X=0,int V=0){x=X;v=V;}
bool operator > (const state &p)const{return v>p.v;}
};
priority_queue<state,vector<state>,greater<state> >qmin;
int dis[2][N+9],vis[N+9];
void Dijkstra(int id,int st){
for (int i=1;i<=n;++i) dis[id][i]=INF,vis[i]=0;
dis[id][st]=0;qmin.push(state(st,0));
while (!qmin.empty()){
int t=qmin.top().x;qmin.pop();
if (vis[t]) continue;
vis[t]=1;
for (int i=lin[id][t];i;i=e[i].next)
if (dis[id][t]+e[i].v<dis[id][e[i].y])
qmin.push(state(e[i].y,dis[id][e[i].y]=dis[id][t]+e[i].v));
}
}
int deg[N+9];
queue<int>q;
void Topsort(){
for (int i=1;i<=n;++i) deg[i]=0,vis[i]=1;
for (int i=1;i<=n;++i)
for (int j=lin[0][i];j;j=e[j].next)
if (!e[j].v) ++deg[e[j].y];
for (int i=1;i<=n;++i)
if (!deg[i]) q.push(i);
while (!q.empty()){
int t=q.front();q.pop();
vis[t]=0;
for (int i=lin[0][t];i;i=e[i].next)
if (!e[i].v&&!(--deg[e[i].y])) q.push(e[i].y);
}
}
int dp[N+9][K+9];
int Dfs_dp(int k,int v){
if (v<0||v>sk) return 0;
if (dp[k][v]^-1) return dp[k][v];
dp[k][v]=0;
for (int i=lin[1][k];i;i=e[i].next)
sadd(dp[k][v],Dfs_dp(e[i].y,v+dis[0][k]-e[i].v-dis[0][e[i].y]));
return dp[k][v];
}
int ans;
Abigail into(){
scanf("%d%d%d%d",&n,&m,&sk,&mod);
for (int i=1;i<=n;++i) lin[0][i]=lin[1][i]=0;
cs=0;
for (int i=1;i<=m;++i){
int x,y,v;
scanf("%d%d%d",&x,&y,&v);
Ins(0,x,y,v);Ins(1,y,x,v);
}
}
Abigail work(){
Dijkstra(0,1);
Dijkstra(1,n);
for (int i=1;i<=n;++i)
vis[i]=dis[0][i]+dis[1][i]-dis[0][n]<=sk;
Topsort();
for (int i=1;i<=n;++i)
if (vis[i]&&dis[0][i]+dis[1][i]<=dis[0][n]+sk) {ans=-1;return;}
for (int i=1;i<=n;++i)
for (int j=0;j<=sk;++j) dp[i][j]=-1;
dp[1][0]=1;
ans=0;
for (int i=0;i<=sk;++i)
sadd(ans,Dfs_dp(n,i));
}
Abigail outo(){
printf("%d\n",ans);
}
int main(){
int T;
scanf("%d",&T);
while (T--){
into();
work();
outo();
}
return 0;
}