跳转至

luogu3953 逛公园

题解

与路径统计很像的一道题。

f[i][k]为起点到i点,距离与到其的最短路偏差(肯定大于d[i]d[i]1i的最短路)为k时的路径数。

然后可以拓扑排序后按照k从小到大的对每个点进行dp:

设有边(u,v,w),转移方程可以写成f[v][d[u]+w+k-d[v]]+=f[u][k]

顺序很重要,需要先枚举k然后再枚举点和边。

程序

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#define MAX_N 100007
#define MAX_M 200007
#define MAX_W 57

int N, M, K, P;
int head[MAX_N], nxt[MAX_M], to[MAX_M], dis[MAX_M], cap;
int d[MAX_N];
int deg[MAX_N], vertex[MAX_N], top;
int f[MAX_N][MAX_W];
bool inq[MAX_N];
std::queue<int> q;

inline int read () {
    int x = 0;
    char c = getchar();
    while (c < '0' || '9' < c)
        c = getchar();
    while ('0' <= c && c <= '9')
        x = x * 10 + c - '0', c = getchar();
    return x;
}

void addEdge (int u, int v, int w) {
    nxt[++cap] = head[u];
    head[u] = cap;
    to[cap] = v;
    dis[cap] = w;
}

void init () {
    memset(head, 0, sizeof(head));
    memset(deg, 0, sizeof(deg));
    memset(inq, false, sizeof(inq));
    memset(vertex, 0, sizeof(vertex));
    memset(f, 0, sizeof(f));
    cap = top = 0;
}

void spfa () {
    for (int i = 1;i <= N; ++i)
        d[i] = 2e9;
    d[1] = 0;
    q.push(1), inq[1] = true;
    while (!q.empty()) {
        int x = q.front(); q.pop();
        for (int i = head[x]; i; i = nxt[i]) {
            if (d[to[i]] > d[x] + dis[i]) {
                d[to[i]] = d[x] + dis[i];
                if (!inq[to[i]])
                    q.push(to[i]), inq[to[i]] = true;
            }
        }
        inq[x] = false;
    }
}

inline bool topSort () {
    top = 0;
    for (int i = 1;i <= N; ++i)
        for (int j = head[i]; j; j = nxt[j])
            if (d[i] + dis[j] == d[to[j]])
                deg[to[j]]++;
    for (int i = 1;i <= N; ++i)
        if (!deg[i])
            vertex[++top] = i, q.push(i);
    while (!q.empty()) {
        int x = q.front(); q.pop();
        for (int i = head[x]; i; i = nxt[i]) {
            if (d[to[i]] == d[x] + dis[i]) {
                deg[to[i]]--;
                if (!deg[to[i]]) {
                    vertex[++top] = to[i];
                    q.push(to[i]);
                }
            }
        }
    }
    return top == N;
}

void dp () {
    memset(f, 0, sizeof(f));
    f[1][0] = 1;
    for (int k = 0;k <= K; ++k) {
        for (int i = 1;i <= N; ++i) {
            int x = vertex[i];
            for (int j = head[x]; j; j = nxt[j]) {
                if (d[x] + dis[j] + k - d[to[j]] <= K) {
                    (f[to[j]][d[x] + dis[j] + k - d[to[j]]] += f[x][k]) %= P;
                }
            }
        }
    }
    int res = 0;
    for (int i = 0;i <= K; ++i)
        (res += f[N][i]) %= P;
    printf("%d\n", res % P);
}

void solve () {
    N = read(), M = read(), K = read(), P = read();
    int u, v, w;
    for (int i = 1;i <= M; ++i) {
        u = read(), v = read(), w = read();
        addEdge(u, v, w);
    }
    spfa();
    if (!topSort()) {puts("-1");return;}
    dp();
}

int main () {
    int T = read();
    while (T--) init(), solve();
    return 0;
}