【傻逼】wxr
看到 \(k=50\),考虑按路径长度为一维度 dp。
设 \(f_{u,k}\) 为 从 \(1\) 到 \(u\) 路径长度为 \(d_u+k\) 的总方案数。
需要以倒拓扑序的顺序进行转移。
对于图上的每条边 \(e(u,v)\),有转移方程:
\(f_{u,k}=\sum\limits_{e(u,v)}f_{v,k-(d_u + w - d_v)} \ (d_u+w-d_v<k)\)。
其中 \(d_u+w_{u,v}-d_v\) 为较 \(d_v\) 经过 \(e(u,v)\) 多走的路程。
考虑按路径长度进行记忆化搜索求解。
具体实现可以建反图后搜索。
注:在搜索过程中如果遇见 \(0\) 环,则存在无穷种路径,输出 \(-1\)。可以通过维护每个点在当前路径长度被搜索到的次数判环。
代码没写。主要是不会。
写了。
#include <bits/stdc++.h>
typedef long long ll;
const int maxn = 2e6 + 20;
const int maxm = 4e6 + 20;
const int maxk = 55;
const ll inf = 4e18;
ll dis[maxn];
bool mk[maxn];
int n, m, k, mod;
ll f[maxn][maxk];
int vis[maxn][maxk];
ll ans = 0;
bool inLoop;
struct Graph {
struct edge { int u, v, w, next; } e[maxn];
int head[maxn], idx;
void add(int x, int y, int z){
idx++; e[idx].u = x, e[idx].v = y, e[idx].w = z;
e[idx].next = head[x], head[x] = idx;
}
void clear() { memset(head, 0, sizeof head); idx = 0; }
} G, revG;
struct point{
int u; ll dis;
friend bool operator < (point a, point b){
return a.dis > b.dis;
}
point(int a, ll b){
u = a, dis = b;
}
};
void dijkstra(int st, Graph &G){
std::priority_queue<point> q;
dis[st] = 0; q.push(point(st, 0));
while(!q.empty()){
int u = q.top().u; q.pop();
if(mk[u]) continue;
mk[u] = 1;
for(int i = G.head[u]; i; i = G.e[i].next){
int v = G.e[i].v, w = G.e[i].w;
if(dis[v] > dis[u] + w){
dis[v] = dis[u] + w;
q.push(point(v, dis[v]));
}
}
}
}
int dfs(int u, int dk, Graph &G) {
if(dk < 0 || dk > k) return 0;
if(f[u][dk] != -1) return f[u][dk];
f[u][dk] = 0, vis[u][dk] = 1;
for(int i = G.head[u]; i; i = G.e[i].next) {
int v = G.e[i].v, w = G.e[i].w, len = dk - (dis[v] + w - dis[u]);
if(len < 0 || len > k) continue;
if(vis[v][len]) { inLoop = 1; return 0; }
f[u][dk] += dfs(v, len, G);
if(f[u][dk] >= mod) f[u][dk] -= mod;
}
vis[u][dk] = 0;
return f[u][dk];
}
void init() {
ans = 0, inLoop = 0;
G.clear(), revG.clear();
for(int i = 0; i <= n + 1; i++)
for(int j = 0; j <= k; j++)
f[i][j] = -1, vis[i][j] = 0;
for(int i = 1; i <= n + 1; i++) dis[i] = inf, mk[i] = 0;
}
void solve() {
scanf("%d %d %d %d", &n, &m, &k, &mod);
init();
for(int i = 1; i <= m; i++){
int x, y, z;
scanf("%d %d %d", &x, &y, &z);
G.add(x, y, z), revG.add(y, x, z);
}
G.add(n + 1, 1, 0), revG.add(1, n + 1, 0);
dijkstra(n + 1, G);
f[n + 1][0] = 1;
for(int i = 0; i <= k; i++) {
ans += dfs(n, i, revG);
if(ans >= mod) ans -= mod;
}
printf("%lld\n", inLoop ? -1 : ans);
}
int main(){
int t; scanf("%d", &t);
while(t--) solve();
return 0;
}