[NOIP2017]逛公园
[NOIP2017]逛公园https://www.luogu.org/problemnew/show/P3953
题目描述
策策同学特别喜欢逛公园。公园可以看成一张 \(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 条合法路径。
测试数据与约定
对于\(100\)%的数据,$ 1≤P≤10^9,1≤a_i,b_i≤N,0≤c_i≤1000$
数据保证:至少存在一条合法的路线。
建反图记忆化搜索
先一边\(Spfa()\)预处理出最短路\(dist[i]\)
设\(dp[i][j]\)表示从点\(1\)到点\(i\)距离等于\(dist[i]+j\)的路径条数
有:
对于一条反边\((u,v,w)\)
判零环:
如果一个未确定的\(dp\)值被经过两次,显然有零环,标记数组\(c[i][j]\)记录即可
#define RG register
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int N=1e5+5,M=2e5+5;
inline int read()
{
RG int x=0,w=1;RG char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*w;
}
int T,n,K,m,p,cnt,Ans;
int last[N],Last[N],dist[N],f[N][55];
bool Flag;
bool used[N],c[N][55];
struct edge{int to,next,w;}e[M],E[M];
queue<int> Q;
inline void insert(int u,int v,int w)
{
cnt++;
e[cnt]=(edge){v,last[u],w};last[u]=cnt;
E[cnt]=(edge){u,Last[v],w};Last[v]=cnt;
}
inline void init()
{
cnt=0;Ans=0;Flag=false;
memset(last,0,sizeof(last));
memset(Last,0,sizeof(Last));
memset(dist,63,sizeof(dist));
memset(used,false,sizeof(used));
memset(f,-1,sizeof(f));
memset(c,false,sizeof(c));
memset(e,0,sizeof(e));
memset(E,0,sizeof(E));
}
inline void Spfa()
{
while(!Q.empty())Q.pop();
Q.push(1);
used[1]=true;dist[1]=0;
while(!Q.empty())
{
int now=Q.front();Q.pop();
used[now]=false;
for(RG int i=last[now];i;i=e[i].next)
{
int v=e[i].to;
if(dist[v]>dist[now]+e[i].w)
{
dist[v]=dist[now]+e[i].w;
if(!used[v])Q.push(v),used[v]=true;
}
}
}
}
int dfs(int now,int k)
{
if(~f[now][k])return f[now][k];
c[now][k]=true;
f[now][k]=0;
for(RG int i=Last[now];i;i=E[i].next)
{
int v=E[i].to;
int t=dist[now]+k-E[i].w-dist[v];
if(t<0)continue;
if(c[v][t])Flag=true;
f[now][k]+=dfs(v,t);f[now][k]%=p;
}
c[now][k]=false;
return f[now][k];
}
int main()
{
T=read();
while(T--)
{
init();
n=read(),m=read(),K=read(),p=read();
RG int u,v,w;
for(RG int i=1;i<=m;i++)
{
u=read(),v=read(),w=read();
insert(u,v,w);
}
f[1][0]=1;
Spfa();
for(RG int i=0;i<=K;i++)Ans+=dfs(n,i),Ans%=p;
dfs(n,K+1);//判零环[K=0]
if(Flag){puts("-1");continue;}
printf("%d\n",Ans);
}
return 0;
}