[周末训练]逛公园
题目
【题目描述】
策策同学特别喜欢逛公园。 公园可以看成一张$N$个点$M$条边构成的有向图,且没有自环和重边。其中$1$号点是公园的入口,$N$号点是公园的出口,每条边有一个非负权值,代表策策经过这条边所要花的时间。
策策每天都会去逛公园,他总是从$1$号点进去,从$N$号点出来。
策策喜欢新鲜的事物,他不希望有两天逛公园的路线完全一样,同时策策还是一个特别热爱学习的好孩子,他不希望每天在逛公园这件事上花费太多的时间。如果$1$号点到$N$号点的最短路长为$d$,那么策策只会喜欢长度不超过$d+K$的路线。
策策同学想知道总共有多少条满足条件的路线,你能帮帮他吗?
为避免输出过大,答案对$P$取模。
如果有无穷多条合法的路线,请输出$-1$。
【输入格式】
第一行包含一个整数$T$, 代表数据组数。
接下来$T$组数据,对于每组数据:
第一行包含四个整数$N,M,K,P$, 每两个整数之间用一个空格隔开。
接下来$M$行,每行三个整数$a_i,b_i,c_i$, 代表编号为$a_i,b_i$的点之间有一条权值为$c_i$的有向边,每两个整数之间用一个空格隔开。
【输出格式】
输出文件包含$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
【样例解释】
对于第一组数据,最短路为$3$。
$1-5,1-2-4-5,1-2-3-5$为$3$条合法路径。
【数据范围与提示】
对于不同测试点,我们约定各种参数的规模不会超过如下
测试点编号 | $T$ | $N$ | $M$ | $K$ | 是否有$0$边 |
$1$ | $5$ | $5$ | $10$ | $0$ | 否 |
$2$ | $1000$ | $2000$ | |||
$3$ | $50$ | ||||
$4$ | |||||
$5$ | |||||
$6$ | 是 | ||||
$7$ | $100000$ | $200000$ | $0$ | 否 | |
$8$ | $3$ | $50$ | |||
$9$ | 是 | ||||
$10$ |
题解
【做题经历】
刚开始想的是一遍$dij$跑出最短路,然后对于每一条在最短路上的点,用其他的一些边来替换。
后来发现时间复杂度有点不可靠,大概是在$O(NM)$的范围左右。
【正解】
一道最短路+$DP$题。
先设最短路长度为$d$。
那么我们要求的就是长度在$d+K$以内的路径的数量。
首先,如果我们要把每一条路都求出来,那么这是一定不可行的,因为这道题的数据范围比较大。
那么我们应该怎么做这道题呢?
其实与我在做题的时候思路是比较靠近的,但是我想到的是将每一条边进行替换,却没有想到直接进行距离的计算。
我们先将输出$-1$的情况解决了。
其实很简单,就是在某一条合法路径上,有一个$0$环(就是一个环上所有的边权值都为$0$),那么就输出$-1$。
这个情况很简单,难点还是在计算总路径数上。
首先建一个逆图,从终点开始跑一边$dij$,得到每个点$u$到终点的最短距离$dis[u]$。
- 如果$dis[u]+w=dis[v]$,说明$edge[i]$是存在于最短路径上的。
- 如果$dis[u]+w≠dis[v]$,说明$edge[i]$是不存在于最短路径之上的,这个时候我们就要开始考虑替换,考虑有无其他情况。
现在开始处理第二种情况,如果$dis[u]+w-dis[v]≤K$,说明将从点$u$到点$v$的最短路径直接替换为$edge[i]$是合法的,因为这条新的路径与最短路的差即为从$u$到$v$的最短路径的差,而我们又保证了这个差$x$是小于等于$K$的。
当然,如果$dis[u]+w-dis[v]>K$,说明替换后的路径是不合法的,不用管即可。
但是如果我们将每次都存下来再计数,实际上是很费时间的,这个时候我们就要考虑记忆化(或者说是$DP$)。
令$dp[u][k]$:搜索到点$u$的一条路径上,其长度与路径长度上界(即$d+K$)的差值为$k$时,总路径的长度。
最后再整合一下代码即可。
顺带提一句,注意每组数据的初始化。不然你就$WA$穿地球了
#include<bits/stdc++.h> using namespace std; template<class T>inline void qread(T& x){ char c;bool f=false;x=0; while((c=getchar())<'0'||'9'<c)if(c=='-')f=true; for(x=(c^48);'0'<=(c=getchar())&&c<='9';x=(x<<1)+(x<<3)+(c^48)); if(f)x=-x; } template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);} inline int rqread(){ char c;bool f=false;int x=0; while((c=getchar())<'0'||'9'<c)if(c=='-')f=true; for(x=(c^48);'0'<=(c=getchar())&&c<='9';x=(x<<1)+(x<<3)+(c^48)); return f?-x:x; } template<class T>inline T Max(const T x,const T y){return x>y?x:y;} template<class T>inline T Min(const T x,const T y){return x<y?x:y;} template<class T>inline T fab(const T x){return x>0?x:-x;} const int MAXM=200000; const int MAXN=100000; const int MAXK=50; class gragh{ #define EDGE_SIZE 200000 #define NODE_SIZE 100000 public: struct edge{int to,nxt,w; edge(){} edge(const int T,const int N,const int W):to(T),nxt(N),w(W){} }e[EDGE_SIZE+5]; int tail[NODE_SIZE+5],ind; inline void add_edge(const int u,const int to,const int w){e[++ind]=edge(to,tail[u],w);tail[u]=ind;} inline void dijkstra(int dis[],const int s,const int N){ struct node{int u,w; node(){} node(const int U,const int W):u(U),w(W){} }; struct cmp{ bool operator()(const node a,const node b){return !(a.w<b.w);} }; for(int i=1;i<=N;++i)dis[i]=0x3f3f3f3f; priority_queue<node,vector<node>,cmp>Q; Q.push(node(s,dis[s]=0)); while(!Q.empty()){ int u=Q.top().u;Q.pop(); for(int i=tail[u],v;i;i=e[i].nxt)if(dis[v=e[i].to]>dis[u]+e[i].w) Q.push(node(v,(dis[v]=dis[u]+e[i].w))); } } inline void clr(){memset(tail,ind=0,sizeof tail);} gragh(){clr();} }G,rG; int T,N,M,K,P,a,b,c; int dis[MAXN+5],dp[MAXN+5][MAXK+5]; bool in[MAXN+5][MAXK+5]; int dfs(const int u,const int k){ if(in[u][k])return -1; if(dp[u][k])return dp[u][k]; in[u][k]=true;dp[u][k]=(u==N)?1:0; for(int i=G.tail[u],v,w,tmp;i;i=G.e[i].nxt)if((tmp=dis[v=G.e[i].to]-dis[u]+G.e[i].w)<=k){ if((w=dfs(v,k-tmp))==-1)return dp[u][k]=-1; (dp[u][k]+=w)%=P; } return in[u][k]=false,dp[u][k]; } void init(){ memset(dp,0,sizeof dp); memset(in,false,sizeof in); G.clr(),rG.clr(); } signed main(){ qread(T); while(T--){ init(); qread(N,M,K,P); while(M--)qread(a,b,c),G.add_edge(a,b,c),rG.add_edge(b,a,c); rG.dijkstra(dis,N,N); printf("%d\n",dfs(1,K)); } return 0; }