Problem
给一张有向图(无重边、自环,如果有的话应该会麻烦许多吧)。求从 1 1 ~有多少种走的方案?其中每一种方案中,走的距离均不超过最短路+K。
多组测试数据+此题数据范围=NOIP2017卡常神题 多 组 测 试 数 据 + 此 题 数 据 范 围 = N O I P 2017 卡 常 神 题 。
Solution
提供一种优美的做法——记忆化搜索。
建一个正图、一个反图。跑一次反向最短路(常用技巧)。
f[u][k]
f
[
u
]
[
k
]
表示
dis(u,n)≤MinDis(u,n)+K
d
i
s
(
u
,
n
)
≤
M
i
n
D
i
s
(
u
,
n
)
+
K
的方案数,那么答案就是
f[1][K]
f
[
1
]
[
K
]
。
先列出每次的转移方程。
f[u][k]=∑f[v][k−(MinDis(v,n)−MinDis(u,n)+val(u−>v))]
f
[
u
]
[
k
]
=
∑
f
[
v
]
[
k
−
(
M
i
n
D
i
s
(
v
,
n
)
−
M
i
n
D
i
s
(
u
,
n
)
+
v
a
l
(
u
−
>
v
)
)
]
那如何判0环呢?
每次dfs的时候,如果还没做完,在它的转移中又搜到它自己了,即是无解。可以开一个
instack
i
n
s
t
a
c
k
数组来记录一下。
跑
dijkstra
d
i
j
k
s
t
r
a
用线段树维护最小值,不要用
priority
p
r
i
o
r
i
t
y
的那个堆,太慢。
甚至我用了常数更小的
zkw
z
k
w
。
#include <cstdio>
#include <cstring>
#define N 100010
#define M 200010
#define re register int
#define fp(i, a, b) for(re i = a, I = b; i <= I; ++i)
struct edge {int to, val, next;}e[M<<1];
int T, n, m, K, P, f[N][51], cnt = 1;
int dis[N], head1[N], headn[N];
bool in[N][51];
inline char gc() {
static char now[1<<16], *S, *T;
if(S == T) {T = (S = now) + fread(now, 1, 1<<16, stdin); if(S == T) return EOF;}
return *S++;
}
inline int read() {
int x = 0, f = 1; char c = gc();
while(c < '0' || c > '9') {if(c == '-') f = -1; c = gc();}
while(c >= '0' && c <= '9') {x = x * 10 + c - 48; c = gc();}
return x * f;
}
namespace zkw {
int t[N<<2], sgt;
inline void Set(re n) {sgt = 1; while(sgt <= n) sgt<<= 1; --sgt; t[0] = N - 1;}
inline void clr() {fp(i, 1, (sgt<<1) + 1) t[i] = 0;}
inline int cmp(const re &a, const re &b) {return dis[a] < dis[b] ? a : b;}
inline void mdy(re x, re w) {for(re i = x + sgt; dis[t[i]] > w; i>>= 1) t[i] = x; dis[x] = w;}
inline void del(re x) {t[x+= sgt] = 0; x>>= 1; while(x) t[x] = cmp(t[x<<1], t[x<<1|1]), x>>= 1;}
}
using namespace zkw;
inline void dij() {
memset(dis, 9, 4 * (n + 1)); clr(); mdy(n, 0);
fp(j, 1, n) {
re u = t[1]; del(u);
for(re i = headn[u]; i; i = e[i].next)
if(dis[e[i].to] > dis[u] + e[i].val) mdy(e[i].to, dis[u] + e[i].val);
}
}
inline void add(re u, re v, re w, re *fi) {e[++cnt] = (edge){v, w, fi[u]}; fi[u] = cnt;}
inline void mod(re &a) {a>= P ? a-= P : 0;}
int dfs(re u, re k) {
if(in[u][k]) return -1;
if(f[u][k]) return f[u][k];
in[u][k] = 1; u == n ? f[u][k] = 1 : 0;
for(re i = head1[u], temp; i; i = e[i].next) {
if((temp = dis[e[i].to] - dis[u] + e[i].val) <= k) {
int w;
if((w = dfs(e[i].to, k - temp)) == -1) return f[u][k] = -1;
mod(f[u][k]+= w);
}
}
return in[u][k] = 0, f[u][k];
}
int main() {
T = read();
while(T--) {
cnt = 1;
memset(f, 0, sizeof(f)); memset(in, 0, sizeof(in));
n = read(); m = read(); K = read(); P = read();
Set(n); re u, v, w;
memset(head1, 0, 4 * (n + 1)); memset(headn, 0, 4 * (n + 1));
while(m--) u = read(), v = read(), w = read(), add(u, v, w, head1), add(v, u, w, headn);
dij(); printf("%d\n", dfs(1, K));
}
return 0;
}