P3953 [NOIP2017 提高组] 逛公园
f[u][i]表示1~u的距离为其最短路+i的方案数,如果有0环则标记inf=1
dp时使用在反图上记忆化搜索并使用是否在栈中判断0环
点击查看代码
#include <queue>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int N = 1e5 + 5, M = 4e5 + 5, K = 55;
int T, n, m, k, p, h1[N], h2[N], e[M], w[M], nxt[M], idx;
int dist[N], f[N][K]; bool st[N], inf, in_stk[N][K], vis[N][K];
void add(int *h, int a, int b, int c) {
e[++ idx] = b, w[idx] = c, nxt[idx] = h[a], h[a] = idx;
}
void Dijkstra() { // 求出最短路
typedef std::pair<int, int> PII;
std::priority_queue<PII, std::vector<PII>, std::greater<PII> > heap;
memset(dist + 1, 0x3f, n << 2), memset(st + 1, 0, n);
heap.push({dist[1] = 0, 1});
while(heap.size()) {
int u = heap.top().second; heap.pop();
if(st[u]) continue;
st[u] = true;
for(int i = h1[u]; i; i = nxt[i]) {
int v = e[i];
if(dist[v] > dist[u] + w[i])
heap.push({dist[v] = dist[u] + w[i], v});
}
}
}
int dp(int u, int x) {
if(in_stk[u][x]) return inf = true, 0; // 在栈中
if(vis[u][x]) return f[u][x];
in_stk[u][x] = vis[u][x] = true;
f[u][x] = 0;
for(int i = h2[u]; i; i = nxt[i]) {
int v = e[i], x2 = dist[u] + x - w[i] - dist[v]; // x2为需要的x
if(0 <= x2 && x2 <= k)
f[u][x] += dp(v, x2), f[u][x] >= p && (f[u][x] -= p);
if(inf) return 0;
}
if(u == 1 && !x) f[u][x] = 1; // 初始条件
in_stk[u][x] = false;
return f[u][x];
}
int main() {
scanf("%d", &T);
while(T --) {
scanf("%d%d%d%d", &n, &m, &k, &p), idx = 0;
memset(h1 + 1, 0, n << 2), memset(h2 + 1, 0, n << 2);
for(int i = 0, a, b, c; i < m; i ++)
scanf("%d%d%d", &a, &b, &c), add(h1, a, b, c), add(h2, b, a, c);
Dijkstra(), inf = false;
for(int i = 1; i <= n; i ++) memset(vis[i], 0, k + 1);
int res = 0;
for(int x = 0; x <= k && !inf; x ++) {
for(int i = 1; i <= n; i ++) memset(in_stk[i], 0, k + 1); // 清空栈
res += dp(n, x), res >= p && (res -= p);
}
if(inf) puts("-1");
else printf("%d\n", res);
}
return 0;
}