讲实话,这题我觉得比D2T3难,糊脸
一句话题意,让你求长度小于MinDis(1,n)+k的路径条数,若有无穷条,输出-1
话说看完题第一反应是为什么会有无穷条,后来看见数据有零环,感觉人生崩塌了
先无脑跑一个最短路,求出最短路,以及每个点到1的最小距离,可以spfa,不卡
然后因为题目要求一定要回到N点,那么我们反向建图,从N开始bfs跑出能到N的每个点
这样后面遇到不合法的直接continue好了
接下来是重点,怎么设计dp,其实数据范围给了启发,n=100000,k=50,妥妥的
那么怎么设计状态和转移呢,我个人是这样理解的,既然是长度小于dis(1,n)+k
那么这条路径肯定是在最短路上选一个点分叉出去,那么这就会产生一些冗余路径
我们记录冗余路径长度,小于K就可以继续dfs,不然直接走接下来的最短路径好了
那么dp[i][j]表示当前i点还剩j可以浪费时的符合要求的路径条数
所以对于边i
d
p
[
t
o
i
]
[
j
]
+
=
d
p
[
f
r
o
m
i
]
[
j
+
w
i
−
(
d
i
s
[
t
o
i
]
−
d
i
s
[
f
r
o
m
i
]
)
]
dp[to_i][j]+=dp[from_i][ j+w_i-(dis[to_i]-dis[from_i]) ]
dp[toi][j]+=dp[fromi][j+wi−(dis[toi]−dis[fromi])]
那么0环怎么判断呢,因为是记忆化循环,0环就会死循环,特判一下好了
过程大概是
最
短
路
−
>
可
行
点
−
>
记
忆
化
搜
索
最短路->可行点->记忆化搜索
最短路−>可行点−>记忆化搜索
代码,人丑常数大,会被卡一个点,只能开O2过了
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
const int maxn = 100007;
const int INF = 2147483647;
const int maxk = 51;
int n,m,k,p,len,dis[maxn],alive[maxn];
struct node
{
int to,next,w;
}edge[maxn*2],abedge[maxn*2];
int cnt,head[maxn],abcnt,abhead[maxn],ans[maxn][maxk],vis[maxn][maxk];
inline int read()
{
char ch = getchar();int ret=0;
while(ch<'0'||ch >'9') ch=getchar();
while(ch<='9'&&ch>='0') ret=ret*10+ch-'0',ch=getchar();
return ret;
}
void add(int from,int to,int w)
{
edge[++cnt].to=to;
edge[cnt].next=head[from];
edge[cnt].w=w;
head[from]=cnt;
abedge[++abcnt].to=from;
abedge[abcnt].next=abhead[to];
abedge[abcnt].w=w;
abhead[to]=abcnt;
}
void spfa()
{
queue<int>q;
int used[maxn];
for(int i=1;i<=n;i++)dis[i]=INF;
memset(used,0,sizeof(used));
dis[1]=0;used[1]=1;
q.push(1);
while(!q.empty())
{
int f1=q.front();
q.pop();used[f1]=0;
for(int i=head[f1];i;i=edge[i].next)
{
int to=edge[i].to,w=edge[i].w;
if(dis[to]>dis[f1]+w)
{
dis[to]=dis[f1]+w;
if(!used[to])q.push(to),used[to]=1;
}
}
}
}
void bfs()
{
int used[maxn];
memset(used,0,sizeof(used));
queue<int>qp;
qp.push(n);used[n]=1;
while(!qp.empty())
{
int f1=qp.front();
qp.pop();
alive[f1]=1;
for(int i=abhead[f1];i;i=abedge[i].next)
{
int to=abedge[i].to;
if(!used[to])qp.push(to),used[to]=1;
}
}
}
int dps(int u,int rest)
{
if(rest<0)return 0;
else if(vis[u][rest])return -INF;
else if(ans[u][rest]!=-1)return ans[u][rest];
else
{
int re=u==n?1:0;
vis[u][rest]=1;
for(int i=head[u];i;i=edge[i].next)
{
int to=edge[i].to;
if(!alive[to])continue;
int w=edge[i].w;
int waste=w-dis[to]+dis[u];
int ret=dps(to,rest-waste);
if(ret==-INF)return -INF;
else
re=(re+ret)%p;
}
ans[u][rest]=re;
vis[u][rest]=0;
return re;
}
}
int main()
{
int T;
T=read();
while(T--)
{
memset(alive,0,sizeof(alive));
memset(head,0,sizeof(head));
memset(abhead,0,sizeof(abhead));
memset(edge,0,sizeof(edge));
memset(abedge,0,sizeof(abedge));
memset(vis,0,sizeof(vis));
memset(ans,-1,sizeof(ans));
cnt=abcnt=0;
n=read(),m=read(),k=read(),p=read();
for(int i=1;i<=m;i++)
{
int x,y,z;
x=read(),y=read(),z=read();
add(x,y,z);
}
spfa(),bfs();
int ans1=dps(1,k);
if(ans1!=-INF)cout<<ans1<<endl;
else cout<<"-1"<<endl;
}
}