[Luogu P3953] [UOJ 331] [NOIP 2017 tg]逛公园

版权声明:欢迎转载蒟蒻博客,但请注明出处: https://blog.csdn.net/LPA20020220/article/details/82975402

洛谷传送门

UOJ传送门

题目描述

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

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

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

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

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

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

输入输出格式

输入格式:

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

接下来 T T 组数据,对于每组数据: 第一行包含四个整数 N , M , K , P N,M,K,P ,每两个整数之间用一个空格隔开。

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

输出格式:

输出文件包含 T T 行,每行一个整数代表答案。

输入输出样例

输入样例#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】

对于第一组数据,最短路为 3 3 1 5 , 1 2 4 5 , 1 2 3 5 1 – 5, 1 – 2 – 4 – 5, 1 – 2 – 3 – 5 3 3 条合法路径。

对于 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

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

解题分析

看到 k k 这个微妙的大小, 就知道大概复杂度和 k k 相关。

如果在没有零边的情况下, 我们在最短路模型下再跑附加边, 等于在跑一个分为 k k 层的 D A G DAG 图, 因为没有后效性, 所以这样计算方案数是可以记忆化 D F S DFS 的。

现在考虑有零环的情况, 实际上就是跑回了同一个点的同一层, 记录一下当前这个点是否在栈中就好了。

总复杂度 O ( α N + k N ) O(\alpha N+kN)

代码如下:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cctype>
#include <cmath>
#include <queue>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 200500
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
int head[MX], head2[MX], dis[MX], dp[MX][51];
bool vis[MX], inq[MX][51];
struct Edge {int to, len, nex;} edge[MX << 2];
int cnt, dot, line, k, MOD;
IN void add(R int from, R int to, R int len)
{edge[++cnt] = {to, len, head[from]}, head[from] = cnt;}
IN void addr(R int from, R int to, R int len)
{edge[++cnt] = {to, len, head2[from]}, head2[from] = cnt;}
std::queue <int> q;
void SPFA()
{
	q.push(dot); std::memset(dis, 63, sizeof(dis));
	dis[dot] = 0; R int now;
	W (!q.empty())
	{
		now = q.front(); q.pop();
		for (R int i = head2[now]; i; i = edge[i].nex)
		{
			if(dis[edge[i].to] > dis[now] + edge[i].len)
			{
				dis[edge[i].to] = dis[now] + edge[i].len;
				if(!vis[edge[i].to]) vis[edge[i].to] = true, q.push(edge[i].to);
			}
		}
		vis[now] = false;
	}
}
int DFS(R int now, R int avai)
{
	if(inq[now][avai]) return -1;
	if(dp[now][avai]) return dp[now][avai];
	inq[now][avai] = true;
	dp[now][avai] = (now == dot);
	R int val, del;
	for (R int i = head[now]; i; i = edge[i].nex)
	{
		if((del = dis[edge[i].to] - dis[now] + edge[i].len) <= avai)
		{
			if((val = DFS(edge[i].to, avai - del)) == -1)
			return -1;
			(dp[now][avai] += val) %= MOD;
		}
	}
	return inq[now][avai] = false, dp[now][avai];
}
int main(void)
{
	int T, a, b, c;
	in(T);
	W (T--)
	{
		in(dot), in(line), in(k), in(MOD);
		std::memset(head, 0, sizeof(head));
		std::memset(inq, 0, sizeof(inq));
		std::memset(head2, 0, sizeof(head2));
		std::memset(dp, cnt = 0, sizeof(dp));
		for (R int i = 1; i <= line; ++i)
		{
			in(a), in(b), in(c);
			addr(b, a, c), add(a, b, c);
		}
		SPFA();
		printf("%d\n", DFS(1, k));
	}
}

猜你喜欢

转载自blog.csdn.net/LPA20020220/article/details/82975402
今日推荐