P3953 逛公园(dp,最短路)
P3953 逛公园
题目描述
策策同学特别喜欢逛公园。公园可以看成一张NN个点MM条边构成的有向图,且没有 自环和重边。其中1号点是公园的入口,NN号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花的时间。
策策每天都会去逛公园,他总是从1号点进去,从NN号点出来。
策策喜欢新鲜的事物,它不希望有两天逛公园的路线完全一样,同时策策还是一个 特别热爱学习的好孩子,它不希望每天在逛公园这件事上花费太多的时间。如果1号点 到NN号点的最短路长为dd,那么策策只会喜欢长度不超过d + Kd+K的路线。
策策同学想知道总共有多少条满足条件的路线,你能帮帮它吗?
为避免输出过大,答案对PP取模。
如果有无穷多条合法的路线,请输出-1−1。
输入输出格式
输入格式:
第一行包含一个整数 TT, 代表数据组数。
接下来TT组数据,对于每组数据: 第一行包含四个整数 N,M,K,PN,M,K,P,每两个整数之间用一个空格隔开。
接下来MM行,每行三个整数a_i,b_i,c_iai,bi,ci,代表编号为a_i,b_iai,bi的点之间有一条权值为 c_ici的有向边,每两个整数之间用一个空格隔开。
输出格式:
输出文件包含 TT 行,每行一个整数代表答案。
输入输出样例
输入样例#1: 复制
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
输出样例#1: 复制
3 -1
说明
【样例解释1】
对于第一组数据,最短路为 33。 $1 – 5, 1 – 2 – 4 – 5, 1 – 2 – 3 – 5$ 为 33 条合法路径。
【测试数据与约定】
对于不同的测试点,我们约定各种参数的规模不会超过如下
测试点编号 | TT | NN | MM | KK | 是否有0边 |
---|---|---|---|---|---|
1 | 5 | 5 | 10 | 0 | 否 |
2 | 5 | 1000 | 2000 | 0 | 否 |
3 | 5 | 1000 | 2000 | 50 | 否 |
4 | 5 | 1000 | 2000 | 50 | 否 |
5 | 5 | 1000 | 2000 | 50 | 否 |
6 | 5 | 1000 | 2000 | 50 | 是 |
7 | 5 | 100000 | 200000 | 0 | 否 |
8 | 3 | 100000 | 200000 | 50 | 否 |
9 | 3 | 100000 | 200000 | 50 | 是 |
10 | 3 | 100000 | 200000 | 50 | 是 |
对于 100%的数据, 1 \le P \le 10^9,1 \le a_i,b_i \le N ,0 \le c_i \le 10001≤P≤109,1≤ai,bi≤N,0≤ci≤1000。
数据保证:至少存在一条合法的路线。
#include<bits/stdc++.h> #define b 1000007 #define ll long long using namespace std; ll n,m,k,p,ans,cnt; ll head[b],dis[b],C[b]; ll vis[b]; struct edge{ ll u,v,net,w; }e[b<<1]; queue<ll>q; inline void add(int u,int v,int w) { e[++cnt].v=v;e[cnt].w=w;e[cnt].net=head[u];head[u]=cnt; } inline ll read() { ll x=0,f=1;char c=getchar(); while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } void spfa(int u) { dis[u]=0;vis[u]=1;C[u]=1;q.push(u); while(!q.empty()) { int now=q.front();q.pop(); vis[now]=0; for(int i=head[now];i;i=e[i].net) { int v=e[i].v; if(dis[v]>dis[now]+e[i].w) { dis[v]=dis[now]+e[i].w; C[v]=C[u]; if(!vis[v]) vis[v]=1,q.push(v); } else if(dis[v]==dis[now]+e[i].w) { if(!vis[v]) vis[v]=1,q.push(v); C[v]+=C[u];C[v]%=p; } } } } void clear() { memset(C,0,sizeof C); memset(dis,127/3,sizeof dis); memset(vis,0,sizeof vis); memset(head,0,sizeof head); memset(e,0,sizeof e); while(!q.empty()) q.pop(); cnt=0; } int main() { int T;T=read(); while(T--) { clear(); n=read();m=read();k=read();p=read(); int x,y,z; for(int i=1;i<=m;i++) { x=read();y=read();z=read(); add(x,y,z); } spfa(1);C[n]%=p; cout<<C[n]<<endl; } }
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<queue> #define N 200007 using namespace std; int T,b,n,m,k,mod,cnt,head[N]; int from[N],to[N],w[N]; int dis[N],vis[N],dp[N][51]; int in[N],re[N],flag; struct edge{ int next,to,w; }e[N<<1]; queue<int>q; inline int read() { int x=0,f=1;char c=getchar(); while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } void add(int x,int y,int w) { e[++cnt]=(edge){head[x],y,w}; head[x]=cnt; } int DFS(int x,int res)//返回到达x点还剩res步可以绕的方案数 { if(~dp[x][res]) return dp[x][res]; dp[x][res]=(x==1); for(int i=head[x];i;i=e[i].next) { int R=e[i].to; int tt=res-((dis[R]+e[i].w)-dis[x]);//通过这条边多绕了一会 if(tt<0) continue; if(in[R]&&re[R]==tt) flag=1;//如果说res一直没有减少的情况下搜出来一个圈,说明有0环 else in[R]=1,re[R]=tt; (dp[x][res]+=DFS(R,tt))%=mod; in[R]=0;re[R]=0; } return dp[x][res]; } void Work() { memset(head,0,sizeof(head));cnt=0; b=read();m=read();k=read();mod=read(); for(int i=1;i<=m;i++) { from[i]=read();to[i]=read();w[i]=read(); add(from[i],to[i],w[i]); } memset(dis,63,sizeof(dis)); q.push(1);dis[1]=0;vis[1]=1; while(!q.empty()) { int x=q.front(); for(int i=head[x];i;i=e[i].next) { int R=e[i].to; if(dis[R]<=dis[x]+e[i].w) continue; dis[R]=dis[x]+e[i].w; if(!vis[R]) vis[R]=1,q.push(R); } q.pop();vis[x]=0; } memset(head,0,sizeof(head));cnt=0; memset(dp,-1,sizeof(dp));flag=0; for(int i=1;i<=m;i++) add(to[i],from[i],w[i]); DFS(b,k); printf("%d\n",flag?-1:DFS(b,k)); } int main() { T=read(); while(T--)Work(); return 0; }
折花枝,恨花枝,准拟花开人共卮,开时人去时。
怕相思,已相思,轮到相思没处辞,眉间露一丝。