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

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. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注