Solution
我们设(dp[i][j])表示到第(i)个点多走了(j)步的方案数,(dis[i])表示从(1)到(i)的最短距离
显然有以下转移方程式,
[dp[i][j] = sum dp[k][dis_i + j - len_{i, k} - dis_k]
]
其中,(k)为所有(i)可以直接到达的节点
最后的那个式子表示的是在(k)这个节点的时候多走的步数,使得再走(len_{i, k})步之后恰好多走(j)步
至于怎么判(0)环,就只要在搜索的时候打一下(vis)标记,看一下有没有搜出环来就可以了
Code
#include <bits/stdc++.h>
using namespace std;
#define fst first
#define snd second
#define squ(x) ((LL)(x) * (x))
#define debug(...) fprintf(stderr, __VA_ARGS__)
typedef long long LL;
typedef pair<int, int> pii;
inline int read() {
int sum = 0, fg = 1; char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') fg = -1;
for (; isdigit(c); c = getchar()) sum = (sum << 3) + (sum << 1) + (c ^ 0x30);
return fg * sum;
}
const int maxn = 1e5 + 10;
const int maxm = 2e5 + 10;
const int maxk = 51;
struct graph {
int Begin[maxn], Next[maxm], To[maxm], w[maxm], e;
void init() { e = -1, memset(Begin, -1, sizeof Begin); }
void add(int x, int y, int z) { To[++e] = y, Next[e] = Begin[x], Begin[x] = e, w[e] = z; }
}g, _g;
int f[maxn][maxk], d[maxn], mod;
bool v[maxn], hk, vis[maxn][maxk];
inline int add(int x, int y) { return (x += y) < mod ? x : x - mod; }
void dijkstra(int s) {
memset(v, 0, sizeof v);
memset(d, 0x3f, sizeof d), d[s] = 0;
priority_queue<pii, vector<pii>, greater<pii> > pq;
pq.push((pii){0, s});
while (!pq.empty()) {
int now = pq.top().snd; pq.pop();
if (v[now]) continue; v[now] = 1;
for (int i = g.Begin[now]; i + 1; i = g.Next[i]) {
int son = g.To[i], ds = g.w[i];
if (d[now] + ds < d[son]) {
d[son] = d[now] + ds;
pq.push((pii){d[son], son});
}
}
}
}
int dfs(int now, int k) {
if (~f[now][k]) return f[now][k];
if (now == 1 && k == 0) f[now][k] = 1;
else f[now][k] = 0;
vis[now][k] = 1;
for (int i = _g.Begin[now]; i + 1; i = _g.Next[i]) {
int son = _g.To[i], ds = _g.w[i], tmp = d[now] + k - ds - d[son];
if (tmp < 0) continue;
if (vis[son][tmp]) hk = 1;
f[now][k] = add(f[now][k], dfs(son, tmp));
}
vis[now][k] = 0;
return f[now][k];
}
int main() {
#ifdef xunzhen
freopen("park.in", "r", stdin);
freopen("park.out", "w", stdout);
#endif
int T = read();
while (T--) {
g.init(), _g.init();
int n = read(), m = read(), k = read(), ans = 0;
mod = read();
for (int i = 1; i <= m; i++) {
int x = read(), y = read(), z = read();
g.add(x, y, z), _g.add(y, x, z);
}
dijkstra(1); memset(f, -1, sizeof f), hk = 0;
for (int i = 0; i <= k; i++) ans = add(ans, dfs(n, i));
printf("%d
", hk ? -1 : ans);
}
return 0;
}
Summary
我去年考试的时候这题爆零,其实就算不会这个DP,也应该要把(30)分的最短路计数打出来