码迷,mamicode.com
首页 > 其他好文 > 详细

[NOIp 2017]逛公园

时间:2017-12-17 00:17:16      阅读:253      评论:0      收藏:0      [点我收藏+]

标签:题解   memset   desc   net   add   size   .net   时间复杂度   font   

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\n"); 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\n", 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 }

 

[NOIp 2017]逛公园

标签:题解   memset   desc   net   add   size   .net   时间复杂度   font   

原文地址:http://www.cnblogs.com/NaVi-Awson/p/8048089.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!