[关闭]
@wsndy-xx 2018-05-05T11:25:13.000000Z 字数 2931 阅读 1367

Noip 2017 逛公园解题报告

                                             wsndy
                                             18.05.03

题解

题面

策策同学特别喜欢逛公园。公园可以看成一张 个点 每条边构成的有向图,且没有 自环和重边。其中 号点是公园的入口, 号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花的时间。
策策每天都会去逛公园,他总是从 号点进去,从 号点出来。
策策喜欢新鲜的事物,它不希望有两天逛公园的路线完全一样,同时策策还是一个特别热爱学习的好孩子,它不希望每天在逛公园这件事上花费太多的时间。如果 号点到 号点的最短路长为 ,那么策策只会喜欢长度不超过 的路线。
策策同学想知道总共有多少条满足条件的路线,你能帮帮它吗?
为避免输出过大,答案对 取模。
如果有无穷多条合法的路线,请输出


题意

给出一张带权有向图,可能有 边,问从 号点到n号点长度不超过最短路长度 的路径有几条,若有无穷多条输出


数据范围


分析

什么时候会有无穷多条?
存在一条长度不超过 的路径,上面可以套一个
也就是说,只有有 边的情况才可能有无穷多条


Task1


如果不存在 边,就是一个很经典的dp了
号点到 号点的最短路, 为最短路条数


然后我们惊喜的发现所有 的测试点都没有 边这样就拿到的


Task2

不存在
然后我们发现这个 非常小
尝试把 放到状态里面去
表示从 号点到 号点长度为 的路径条数


这个状态不是很好转移
转移图是个
因为没有 边, 是严格递减的,不存在环
记忆化搜索即可
时间复杂度 , 可以拿到


Task3

如果有 边会发生什么呢?
可能会出现
环就一定无解吗?
显然不是
回顾一下无穷多解的条件
一条长度 的路径套一个
也就是说 环上有一点,经过它的最短路长度
则有无穷多解
我们记录 表示从 号点出发到 号点的最短路
表示从 号点到 号点的最短路,可以把边反向
那么 表示经过 号点的最短路
开一个 数组记录下每一个 是否在访问中
如果访问了一个已经在访问的说明出现了环
同时满足 的话就是
当然,如果 这个状态肯定用不到
直接抛弃掉即可


Code

  1. #include<cstdio>
  2. #include<cstring>
  3. using namespace std;
  4. const int N = 1e5 + 10, K = 51;
  5. int n, m, k, p, tot, ans = 0;
  6. int first[N], next[N << 1], en[N << 1], w[N << 1];
  7. int first1[N * K], next1[N * K << 1], en1[N * K << 1], d[N * K];
  8. int q[N << 6], dis[N], dis1[N], u[N << 1], v[N << 1], t[N << 1], f[N * K];
  9. bool bz[N];
  10. #define gc getchar()
  11. inline int read() {
  12. int x = 0; char c = gc;
  13. while(c < '0' || c > '9') c = gc;
  14. while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = gc;
  15. return x;
  16. }
  17. inline void insert(int x, int y, int z) {
  18. next[++ tot] = first[x];
  19. first[x] = tot;
  20. en[tot] = y;
  21. w[tot] = z;
  22. }
  23. inline void insert1(int x, int y) {
  24. next1[++ tot] = first1[x];
  25. first1[x] = tot;
  26. en1[tot] = y;
  27. d[y] ++;
  28. }
  29. inline int get(int x, int y) {return (x - 1) * (k + 1) + y + 1;}
  30. int main() {
  31. int T = read();
  32. while(T --) {
  33. n = read(), m = read(), k = read(), p = read();
  34. memset(first, tot = 0, sizeof(first));
  35. for(int i = 1; i <= m; i ++) {
  36. u[i] = read(), v[i] = read(), t[i] = read();
  37. insert(u[i], v[i], t[i]);
  38. }
  39. memset(dis, 60, sizeof(dis));
  40. int l = dis[1] = 0, r = q[1] = 1;
  41. while(l < r) {
  42. int x = q[++ l];
  43. bz[x] = false;
  44. for(int i = first[x]; i; i = next[i])
  45. if(dis[x] + w[i] < dis[en[i]]) {
  46. dis[en[i]] = dis[x] + w[i];
  47. if(! bz[en[i]]) bz[q[++ r] = en[i]] = true;
  48. }
  49. }
  50. memset(first, tot = 0, sizeof(first));
  51. for(int i = 1; i <= m; i ++) insert(v[i], u[i], t[i]);
  52. memset(dis1, 60, sizeof(dis1));
  53. l = dis1[q[r = 1] = n] = 0;
  54. while(l < r) {
  55. int x = q[++ l];
  56. bz[x] = false;
  57. for(int i = first[x]; i; i = next[i])
  58. if(dis1[x] + w[i] < dis1[en[i]]) {
  59. dis1[en[i]] = dis1[x] + w[i];
  60. if(!bz[en[i]]) bz[q[++ r] = en[i]] = true;
  61. }
  62. }
  63. memset(first1, tot = 0, sizeof(first1));
  64. memset(d, 0, sizeof(d));
  65. for(int i = 1; i <= m; i ++) {
  66. int x = get(u[i], 0), y = get(v[i], dis[u[i]] + t[i] - dis[v[i]]);
  67. for(int j = dis[u[i]]; j + t[i] + dis1[v[i]] <= dis[n] + k; j ++, x ++, y ++)
  68. insert1(x, y);
  69. }
  70. int num = (k + 1) * n, sum = 0;
  71. l = r = ans = 0;
  72. memset(f, 0, sizeof(f));
  73. for(int i = 1; i <= num; i ++)
  74. if(!d[i]) q[++ r] = i;
  75. f[1] = 1;
  76. while(l < r) {
  77. int x = q[++ l];
  78. sum ++;
  79. for(int i = first1[x]; i; i = next1[i]) {
  80. if(! -- d[en1[i]]) q[++ r] = en1[i];
  81. f[en1[i]] += f[x];
  82. f[en1[i]] = f[en1[i]] > p ? f[en1[i]] - p : f[en1[i]];
  83. }
  84. }
  85. if(sum < num) printf("-1\n");
  86. else {
  87. for(int i = 0; i <= k; i ++) ans = (ans + f[get(n, i)]) % p;
  88. printf("%d\n", ans);
  89. }
  90. }
  91. return 0;
  92. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注