Description
策策同学特别喜欢逛公园。公园可以看成一张$N$个点$M$条边构成的有向图,且没有 自环和重边。其中1号点是公园的入口,$N$号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花的时间。
策策每天都会去逛公园,他总是从1号点进去,从$N$号点出来。
策策喜欢新鲜的事物,它不希望有两天逛公园的路线完全一样,同时策策还是一个 特别热爱学习的好孩子,它不希望每天在逛公园这件事上花费太多的时间。如果1号点 到$N$号点的最短路长为$d$,那么策策只会喜欢长度不超过$d + K$的路线。
策策同学想知道总共有多少条满足条件的路线,你能帮帮它吗?
为避免输出过大,答案对$P$取模。
如果有无穷多条合法的路线,请输出−1。
Input
第一行包含一个整数 $T$, 代表数据组数。
接下来$T$组数据,对于每组数据: 第一行包含四个整数 $N,M,K,P$,每两个整数之间用一个空格隔开。
接下来$M$行,每行三个整数$a_i,b_i,c_i$,代表编号为$a_i,b_i$的点之间有一条权值为 $c_i$的有向边,每两个整数之间用一个空格隔开。
Output
输出文件包含 $T$ 行,每行一个整数代表答案。
Sample Input
2
5 7 2 10
1 2 1
2 4 0
4 5 2
2 3 2
3 4 1
3 5 2
1 5 3
2 2 0 10
1 2 0
2 1 0
Sample Output
3
-1
Hint
【样例解释1】
对于第一组数据,最短路为 3。 1 – 5, 1 – 2 – 4 – 5, 1 – 2 – 3 – 5 为 3 条合法路径。
【测试数据与约定】
对于不同的测试点,我们约定各种参数的规模不会超过如下
测试点编号 | $T$ | $N$ | $M$ | $K$ | 是否有0边 |
---|---|---|---|---|---|
1 | 5 | 5 | 10 | 0 | 否 |
2 | 5 | 1000 | 2000 | 0 | 否 |
3 | 5 | 1000 | 2000 | 50 | 否 |
4 | 5 | 1000 | 2000 | 50 | 否 |
5 | 5 | 1000 | 2000 | 50 | 否 |
6 | 5 | 1000 | 2000 | 50 | 是 |
7 | 5 | 100000 | 200000 | 0 | 否 |
8 | 3 | 100000 | 200000 | 50 | 否 |
9 | 3 | 100000 | 200000 | 50 | 是 |
10 | 3 | 100000 | 200000 | 50 | 是 |
对于 100%的数据, $1 le P le 10^9,1 le a_i,b_i le N ,0 le c_i le 1000$。
数据保证:至少存在一条合法的路线。
题解(转载)
- 这题如果直接$DP$的话,会发现有后效性,则会重复统计答案。
- 但看到 $k≤50$ ,很小,于是我们考虑拆点。
- 先做一次 $SPFA$,设 $1$ 到 $i$ 号点的最短路为 $dist1[i]$ 。
- 之后把每个点拆成 $k+1$ 个点,分别对应到这个点时的路径长 $j-dist1[i]$ 的值。
- 由于这个值的范围只在 $[0,k]$ 之间,
- 那么我们对于开始时的有向边的两个点拆点,并进行进行连接。
- 这样我们就构成了一个拓扑图,跑一遍拓扑排序即可。
- 当跑完后发现并没有遍历所有点,则直接输出 $-1$ 即可。
- 而且这题还要卡卡常,发现连边时连接了很多无用点,拖慢了拓扑排序的速度。
- 于是我们考虑倒着做一遍 $SPFA$ (从 $n$ 开始),设 $n$ 到 $i$ 号点的最短路为 $dist2[i]$ 。
- 当一个点 $dist1[u[i]]+dist2[v[i]]>dist1[n]+k$ 时,说明这个点就没用了,不需要从它连边出去。
- 时间复杂度 $O(T*M*K)$ 。
1 //Is is made by Awson on 2017.12.16 2 #include <set> 3 #include <map> 4 #include <cmath> 5 #include <ctime> 6 #include <queue> 7 #include <stack> 8 #include <cstdio> 9 #include <string> 10 #include <vector> 11 #include <cstdlib> 12 #include <cstring> 13 #include <iostream> 14 #include <algorithm> 15 #define LL long long 16 #define Max(a, b) ((a) > (b) ? (a) : (b)) 17 #define Min(a, b) ((a) < (b) ? (a) : (b)) 18 #define getnode(x, y) (((x)-1)*(k+1)+(y)) 19 using namespace std; 20 const int N = 100000; 21 const int M = 200000; 22 const int K = 50; 23 int read() { 24 int sum = 0; 25 char ch = getchar(); 26 while (ch < '0' || ch > '9') ch = getchar(); 27 while (ch >= '0' && ch <= '9') sum = (sum<<1)+(sum<<3)+ch-'0', ch = getchar(); 28 return sum; 29 } 30 31 int n, m, p, k, u[M+5], v[M+5], c[M+5]; 32 struct tt { 33 int to, next, cost; 34 }edge[(M*K<<1)+5]; 35 int path[(N*K<<1)+5], top, path2[N+5]; 36 int dist[N+5][2]; 37 bool vis[N+5]; 38 int Q[(N*K<<1)+5], head, tail; 39 int ans[(N*K<<1)+5], in[(N*K<<1)+5]; 40 void add(int u, int v, int c) { 41 edge[++top].to = v; 42 edge[top].cost = c; 43 edge[top].next = path[u]; 44 path[u] = top; 45 } 46 void add2(int u, int v, int c) { 47 edge[++top].to = v; 48 edge[top].cost = c; 49 edge[top].next = path2[u]; 50 path2[u] = top; 51 } 52 void SPFA(int u, int t) { 53 dist[u][t] = 0; 54 memset(vis, 0, sizeof(vis)); vis[u] = 1; 55 Q[head = tail = 0] = u; tail++; 56 while (head < tail) { 57 int u = Q[head]; ++head, vis[u] = 0; 58 for (int i = path2[u]; i; i = edge[i].next) 59 if (dist[edge[i].to][t] > dist[u][t]+edge[i].cost) { 60 dist[edge[i].to][t] = dist[u][t]+edge[i].cost; 61 if (!vis[edge[i].to]) { 62 vis[edge[i].to] = 1; Q[tail] = edge[i].to, ++tail; 63 } 64 } 65 } 66 } 67 void topsort() { 68 memset(ans, 0, sizeof(ans)); ans[0] = 1; 69 int MAX = getnode(n, k), sum = 0; head = tail = 0; 70 for (int i = 0; i <= MAX; ++i) if (!in[i]) Q[tail] = i, ++tail; 71 while (head < tail) { 72 int u = Q[head]; ++head, ++sum; 73 for (int i = path[u]; i; i = edge[i].next) { 74 --in[edge[i].to]; ans[edge[i].to] = (ans[edge[i].to]+ans[u])%p; 75 if (!in[edge[i].to]) Q[tail] = edge[i].to, ++tail; 76 } 77 } 78 if (MAX+1 != sum) { 79 printf("-1 "); return; 80 } 81 int cnt = 0; 82 for (int i = 0; i <= k; i++) 83 cnt = (cnt+ans[getnode(n, i)])%p; 84 printf("%d ", cnt); 85 } 86 87 void work() { 88 n = read(), m = read(), k = read(), p = read(); 89 memset(dist, 127/3, sizeof(dist)); 90 memset(path2, top = 0, sizeof(path2)); 91 for (int i = 1; i <= m; i++) { 92 u[i] = read(), v[i] = read(), c[i] = read(); 93 add2(u[i], v[i], c[i]); 94 } 95 SPFA(1, 0); 96 memset(path2, top = 0, sizeof(path2)); 97 for (int i = 1; i <= m; i++) add2(v[i], u[i], c[i]); 98 SPFA(n, 1); 99 memset(path, top = 0, sizeof(path)); 100 memset(in, 0, sizeof(in)); 101 for (int i = 1; i <= m; i++) { 102 int a = u[i], b = v[i], d = c[i]; 103 if (d <= dist[b][0]-dist[a][0]+k) { 104 int delta = d-(dist[b][0]-dist[a][0]), basea = getnode(a, 0), baseb = getnode(b, delta); 105 for (int j = 0; j <= k-delta && dist[a][0]+dist[b][1]+d+j <= dist[n][0]+k; j++) { 106 add(basea+j, baseb+j, 0); 107 in[baseb+j]++; 108 } 109 } 110 } 111 topsort(); 112 } 113 int main() { 114 int t; cin >> t; 115 while (t--) work(); 116 return 0; 117 }