题目链接
策策同学特别喜欢逛公园。公园可以看成一张 N 个点 M 条边构成的有向图,且没有 自环和重边。其中1号点是公园的入口, N 号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花的时间。
策策每天都会去逛公园,他总是从1号点进去,从 N 号点出来。
策策喜欢新鲜的事物,它不希望有两天逛公园的路线完全一样,同时策策还是一个 特别热爱学习的好孩子,它不希望每天在逛公园这件事上花费太多的时间。如果1号点 到 N 号点的最短路长为 d ,那么策策只会喜欢长度不超过 d+K 的路线。
策策同学想知道总共有多少条满足条件的路线,你能帮帮它吗?
为避免输出过大,答案对 P 取模。
如果有无穷多条合法的路线,请输出−1。
为什么自己当时那么菜,连这题都毫无头绪
这题明显是个DP,由于和路径长度偏移量有关且K很小,很容易想出状态数组是dp[N][K]。
再来想想状态的含义应该是什么。
我们最后要求的是从1号点出发到达n号点路径长度偏移量不超过k的路径总数。
考虑一条合法路径上的点u,那么u到n号点的最短路偏移量也会小于k。在所有u到n号点的路径中偏移量小于 k-当前已偏移量 的都可以进行贡献。
故我们设dp[u][k]表示从点u到n号点路径长度偏移量不超过k的路径条数,那么答案就是dp[1][k] ,我们可以看出这个状态是很好的。
由于是在图上进行DP,所以用记搜实现会更加方便。转移的话由当前走的边算出这次的偏移量d,继续搜索 dp[v][k-d] 即可。
但我们还要考虑无穷多方案的情况。
可以发现只有当在某条合法路径上出现0环时才有无数条路径。
这时因为走一条0环上的边,新的偏移量一定为0,那么我们就会搜到相同状态,因此只需记录一下是否有相同状态同时被搜到即可。
还有一点需要注意的就是边界的设置。不能只把dp[n][0]设为1,因为dp[n][ ]的任何一个都至少为1,也就是说后面的要都加上1。并且可能有直接的一整个0环出现,要在搜到n号点的时候把他的dp值设为1,不然这个无穷多解的情况就漏了!(WA了好久TAT)
代码如下:
#include
#include
#include
#include
#include
#include
#include
#define Set(a,b) memset(a,b,sizeof(a))
using namespace std;
inline int read()
{
int x=0;char ch=getchar();int t=1;
for(;ch>'9'||ch<'0';ch=getchar())if(ch=='-') t=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
return x*t;
}
const int N=1e5+100;
const int M=2e5+100;
struct edge{
int to,next,w;
}a[M],b[M];
int cnt=0;
int head[N];
int he2[N];
int cn=0;
inline void add1(int x,int y,int z){a[++cnt]=(edge){y,head[x],z};head[x]=cnt;}
inline void add2(int x,int y,int z){b[++cn]=(edge){y,he2[x],z};he2[x]=cn;}
queue<int> Q;
int dis[N];bool vis[N];
int t,n,m,k,p;
int dp[N][51];//u到n路径长度不超过 最短路径+k 的方案数
bool in[N][51];//判0环(重复搜相同状态则必为一个0环)
int D;
bool flag;
inline void SPFA()
{
Q.push(n);
while(!Q.empty())
{
register int u=Q.front();Q.pop();
vis[u]=0;
for(register int v,i=head[u];i;i=a[i].next)
{
v=a[i].to;
if(dis[u]+a[i].w>=dis[v]) continue;
dis[v]=dis[u]+a[i].w;
if(!vis[v]) {vis[v]=1;Q.push(v);}
}
}
}
inline int dfs(int u,int len)
{
if(flag) return 0;
if(len<0) return 0;
if(in[u][len]) return (flag=1);
if(dp[u][len]!=-1) return dp[u][len];
dp[u][len]=(u==n? 1:0);//这里再设初状态
in[u][len]=1;
for(register int v,i=he2[u];i&&(!flag);i=b[i].next)
{
v=b[i].to;register int w=b[i].w;
dp[u][len]+=dfs(v,len-(dis[v]+w-dis[u]));
if(dp[u][len]>=p) dp[u][len]-=p;
}
in[u][len]=0;
return dp[u][len];
}
int main()
{
freopen("park.in","r",stdin);
freopen("park.out","w",stdout);
int T=read();
while(T--)
{
n=read();m=read();k=read();Set(head,0);cnt=0;p=read();cn=0;
Set(dis,127/3);dis[n]=0;register int x,y,z;Set(dp,-1);Set(head,0);Set(he2,0);
Set(in,0);
for(register int i=1;i<=m;++i){x=read();y=read();z=read();add2(x,y,z);add1(y,x,z);}
SPFA();flag=0;
if(p==1) {puts("0");continue;}
dfs(1,k);
if(flag) puts("-1");
else printf("%d\n",dp[1][k]%p);
}
}