洛谷传送门
UOJ传送门
题目描述
策策同学特别喜欢逛公园。公园可以看成一张 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 行,每行一个整数代表答案。
输入输出样例
输入样例#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 3。 1 – 5 , 1 – 2 – 4 – 5 , 1 – 2 – 3 – 5 1 – 5, 1 – 2 – 4 – 5, 1 – 2 – 3 – 5 1–5,1–2–4–5,1–2–3–5 为 3 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 1≤P≤109,1≤ai,bi≤N,0≤ci≤1000。
数据保证:至少存在一条合法的路线。
解题分析
看到 k k k这个微妙的大小, 就知道大概复杂度和 k k k相关。
如果在没有零边的情况下, 我们在最短路模型下再跑附加边, 等于在跑一个分为 k k k层的 D A G DAG DAG图, 因为没有后效性, 所以这样计算方案数是可以记忆化 D F S DFS DFS的。
现在考虑有零环的情况, 实际上就是跑回了同一个点的同一层, 记录一下当前这个点是否在栈中就好了。
总复杂度 O ( α N + k N ) O(\alpha N+kN) O(α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));
}
}