题目大意:给定一个有重边,边有权值的无向图。从某一个点出发,求到达所有的点需要的最少费用,
并且限制两点之间只有一条路径。
费用的计算公式为:所有边的费用之和。而边$(x->y)$的费用就为:$y$到初始点的距离$ imes$边权。
题解:记忆化搜索,$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 ", 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; }