LOJ2316「NOIP2017」逛公园

原题链接:https://loj.ac/problem/2316

逛公园

题目描述

策策同学特别喜欢逛公园。 公园可以看成一张 N N N个点 M M M条边构成的有向图,且没有自环和重边。其中 1 1 1号点是公园的入口, N N N号点是公园的出口,每条边有一个非负权值,代表策策经过这条边所要花的时间。

策策每天都会去逛公园,他总是从 1 1 1号点进去,从 N N N号点出来。

策策喜欢新鲜的事物,他不希望有两天逛公园的路线完全一样,同时策策还是一个特别热爱学习的好孩子,他不希望每天在逛公园这件事上花费太多的时间。如果 1 1 1号点到 N N N号点的最短路长为 d d d,那么策策只会喜欢长度不超过 d + K d + K d+K的路线。

策策同学想知道总共有多少条满足条件的路线,你能帮帮他吗?

为避免输出过大,答案对 P P P取模。

如果有无穷多条合法的路线,请输出 − 1 −1 1

输入格式

第一行包含一个整数 T T T, 代表数据组数。

接下来 T T T组数据,对于每组数据:

第一行包含四个整数 N , M , K , P N,M,K,P N,M,K,P, 每两个整数之间用一个空格隔开。

接下来 M M M行,每行三个整数 a i , b i , c i a_i,b_i,c_i ai,bi,ci , 代表编号为 a i , b i a_i,b_i ai,bi的点之间有一条权值为 c i c_i ci 的有向边,每两个整数之间用一个空格隔开。

输出格式

输出文件包含 T T 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 3 3

1 − 5 , 1 − 2 − 4 − 5 , 1 − 2 − 3 − 5 1 - 5, 1 - 2 - 4 - 5, 1 - 2 - 3 - 5 15,1245,1235 3 3 3条合法路径。

数据范围与提示

对于不同测试点,我们约定各种参数的规模不会超过如下

测试点编号 T T T N N N M M M K K K是否有 0 0 0
1 1 1 5 5 5 5 5 5 10 10 10 0 0 0
2 2 2 5 5 5 1000 1000 1000 2000 2000 2000 0 0 0
3 3 3 5 5 5 1000 1000 1000 2000 2000 2000 50 50 50
4 4 4 5 5 5 1000 1000 1000 2000 2000 2000 50 50 50
5 5 5 5 5 5 1000 1000 1000 2000 2000 2000 50 50 50
6 6 6 5 5 5 1000 1000 1000 2000 2000 2000 50 50 50
7 7 7 5 5 5 100000 100000 100000 200000 200000 200000 0 0 0
8 8 8 3 3 3 100000 100000 100000 200000 200000 200000 50 50 50
9 9 9 3 3 3 100000 100000 100000 200000 200000 200000 50 50 50
10 10 10 3 3 3 100000 100000 100000 200000 200000 200000 50 50 50

对于 100 % 100\% 100% 的数据: 1 ≤ P ≤ 1 0 9 , 1 ≤ a i , b i ≤ N , 0 ≤ c i ≤ 1000 1\le P \le 10^9,1 \le a_i,b_i \le N ,0 \le c_i \le 1000 1P109,1ai,biN,0ci1000

数据保证:至少存在一条合法的路线。

题解

看到 K ≤ 50 K\leq 50 K50不禁想到 d p dp dp d p [ i ] [ j ] dp[i][j] dp[i][j]表示第 i i i个点,还剩长度为 j j j的路可以绕的方案数,容易想到记忆化搜索来更新,如果 d f s dfs dfs出环就返回 − 1 -1 1

另外,为了得到绕路的"标准",我们需要每个点到 n n n的最短路,所以需要在反向图上跑一遍以 n n n为起点的最短路。

代码
#include<bits/stdc++.h>
using namespace std;
const int M=1e5+5;
struct sd{int to,w;}ed[M<<2],ed2[M<<2];
bool operator<(sd a,sd b){return a.w>b.w;}
int head[M],nxt[M<<2],head2[M],nxt2[M<<2],dis[M],dp[M][55],T,n,m,k,p,cnt,cnt2;
bool vis[M],vis2[M][55];
priority_queue<sd>dui;
void add(int f,int t,int w){nxt[++cnt]=head[f],head[f]=cnt,ed[cnt]=(sd){t,w};}
void add2(int f,int t,int w){nxt2[++cnt2]=head2[f],head2[f]=cnt2,ed2[cnt2]=(sd){t,w};}
void dijkstra(int s)
{
	memset(vis,0,sizeof(vis));memset(dis,127,sizeof(dis));dui.push((sd){s,dis[s]=0});
	for(sd f;!dui.empty();)
	{
		f=dui.top();dui.pop();if(vis[f.to])continue;vis[f.to]=1;
		for(int i=head[f.to];i;i=nxt[i])if(!vis[ed[i].to]&&dis[ed[i].to]>dis[f.to]+ed[i].w)
		dis[ed[i].to]=dis[f.to]+ed[i].w,dui.push((sd){ed[i].to,dis[ed[i].to]});
	}
}
int dfs(int v,int r)
{
	if(vis2[v][r])return -1;
	if(dp[v][r])return dp[v][r];
	vis2[v][r]=1;if(v==n)dp[v][r]=1;
	for(int i=head2[v],d,w;i;i=nxt2[i])
	if((d=dis[ed2[i].to]-dis[v]+ed2[i].w)<=r)
	{
		if((w=dfs(ed2[i].to,r-d))==-1)return dp[v][r]=-1;
		(dp[v][r]+=w)%=p;
	}
	return vis2[v][r]=0,dp[v][r];
}
void reset(){memset(head,cnt=0,sizeof(head));memset(head2,cnt2=0,sizeof(head2));memset(vis2,0,sizeof(vis2));memset(dp,0,sizeof(dp));}
void in(){scanf("%d%d%d%d",&n,&m,&k,&p);for(int i=1,a,b,c;i<=m;++i)scanf("%d%d%d",&a,&b,&c),add(b,a,c),add2(a,b,c);}
void ac(){dijkstra(n);printf("%d\n",dfs(1,k));}
int main(){for(scanf("%d",&T);T--;)reset(),in(),ac();}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值