题目大意:给定一个有重边,边有权值的无向图。从某一个点出发,求到达所有的点需要的最少费用,
并且限制两点之间只有一条路径。
费用的计算公式为:所有边的费用之和。而边$(x->y)$的费用就为:$y$到初始点的距离$\times$边权。
题解:记忆化搜索,$f[i][j]$表示第$i$个节点,比到这个点的最短多了$j$的方案数,在反图上跑就行了,如果一个点被访问时还在栈中,就有零环
卡点:无(但是可以构造数据卡$SPFA$)
C++ Code:(SFPA)
#include <cstdio>
#include <cstring>
#define maxn 100010
#define maxm 200010
using namespace std;
int Tim, n, m, k, P;
bool flag;
struct Edge {int to, nxt, w;};
struct Graph{
int head[maxn], cnt;
Edge e[maxm];
void add(int a, int b, int c) {
e[++cnt] = (Edge) {b, head[a], c}; head[a] = cnt;
}
} G1, G2;
int dis[maxn], f[maxn][55];
int q[maxn << 2], h, t;
bool vis[maxn], s[maxn][55];
int dfs(int x, int num) {
if (~f[x][num]) return f[x][num];
int ans = 0, v, tmp;
s[x][num] = true;
for (int i = G2.head[x]; i; i = G2.e[i].nxt) {
v = G2.e[i].to;
tmp = dis[x] - dis[v] + num - G2.e[i].w;
if (tmp < 0 || tmp > k) continue;
if (s[v][tmp]) {
flag = true;
return 0;
}
ans = (ans + dfs(v, tmp)) % P;
if (flag) return 0;
}
s[x][num] = false;
return f[x][num] = ans;
}
int solve(int st) {
memset(f, -1, sizeof f);
int ans = 0; flag = false;
f[st][st] = 1;
for (int i = 0; i <= k; i++) {
int tmp = dfs(n, i);
ans = (ans + tmp) % P;
if (flag) break;
}
if (flag) return -1;
else return ans;
}
void SPFA(int st) {
memset(dis, 0x3f, sizeof dis);
memset(vis, false, sizeof vis);
dis[q[h = t = 0] = st] = 0;
while (h <= t) {
int u = q[h++];
vis[u] = false;
for (int i = G1.head[u]; i; i = G1.e[i].nxt) {
int v = G1.e[i].to;
if (dis[v] > dis[u] + G1.e[i].w) {
dis[v] = dis[u] + G1.e[i].w;
if (!vis[v]) {
vis[v] = true;
q[++t] = v;
}
}
}
}
}
int main() {
scanf("%d", &Tim);
while (Tim --> 0) {
scanf("%d%d%d%d", &n, &m, &k, &P);
for (int i = 0; i < m ; i++) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
G1.add(a, b, c);
G2.add(b, a, c);
}
G1.add(0, 1, 0);
G2.add(1, 0, 0);
SPFA(0);
printf("%d\n", solve(0));
if (Tim) {
G1.cnt = G2.cnt = 0;
memset(G1.head, 0, sizeof G1.head);
memset(G2.head, 0, sizeof G2.head);
memset(s, false, sizeof s);
}
}
return 0;
}