[NOIp 2017]逛公园

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 }
时间: 2024-11-05 20:43:39

[NOIp 2017]逛公园的相关文章

[Luogu P3953] 逛公园 (最短路+拓扑排序+DP)

题面 传送门:https://www.luogu.org/problemnew/show/P3953 Solution 这是一道神题 首先,我们不妨想一下K=0,即求最短路方案数的部分分. 我们很容易可以想到一个做法,就是魔改迪杰斯特拉做法: 如果一个点可以更新到达其他点的距离,那个点的方案数就是这个点的方案数:如果一个点所更新出来的距离和之前的相等,那个点的方案数加等当前点的方案数. 用式子可以表现为: f[j]=f[i] (dis[j]>dis[i]+x)   f[j]+=f[i] (dis

TYVJ1427 小白逛公园

P1427 小白逛公园 时间: 1000ms / 空间: 131072KiB / Java类名: Main 描述 小新经常陪小白去公园玩,也就是所谓的遛狗啦…在小新家附近有一条“公园路”,路的一边从南到北依次排着n个公园,小白早就看花了眼,自己也不清楚该去哪些公园玩了.    一开始,小白就根据公园的风景给每个公园打了分-.-.小新为了省事,每次遛狗的时候都会事先规定一个范围,小白只可以选择第a个和第b个公园之间(包括a.b两个公园)选择连续的一些公园玩.小白当然希望选出的公园的分数总和尽量高咯

线段树--小白逛公园nkoj1316

小白逛公园 Time Limit:20000MS  Memory Limit:65536K Case Time Limit:2000MS Description 小新经常陪小白去公园玩,也就是所谓的遛狗啦-在小新家附近有一条"公园路",路的一边从南到北依次排着n个公园,小白早就看花了眼,自己也不清楚该去哪些公园玩了. 一开始,小白就根据公园的风景给每个公园打了分-.-.小新为了省事,每次遛狗的时候都会事先规定一个范围,小白只可以选择第a个和第b个公园之间(包括a.b两个公园)选择连续的

Luogu P3953【NOIP2017】逛公园【最短路+拓扑排序+动态规划】

题目描述 策策同学特别喜欢逛公园.公园可以看成一张NN个点MM条边构成的有向图,且没有 自环和重边.其中1号点是公园的入口,NN号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花的时间. 策策每天都会去逛公园,他总是从1号点进去,从NN号点出来. 策策喜欢新鲜的事物,它不希望有两天逛公园的路线完全一样,同时策策还是一个 特别热爱学习的好孩子,它不希望每天在逛公园这件事上花费太多的时间.如果1号点 到NN号点的最短路长为dd,那么策策只会喜欢长度不超过d + Kd+K的路线. 策策

luogu 3953 逛公园

noip2017 D1T3 逛公园 某zz选手看到数据范围直接就最短路计数了,结果写错了爆零 题目大意: N个点M条边构成的有向图,且没有自环和重边.其中1号点是起点,N号点是公园的终点,每条边有一个非负权值, 代表经过这条边所要花的时间 如果1号点到N号点的最短路长为d,那么策策只选择长度不超过d + K的路线 求总共有多少条满足条件的路线 为避免输出过大,答案对P取模. 如果有无穷多条合法的路线,请输出?1 思路: 首先需要求出最短路用spfa 然后我们dfs的时候dp 具体见注释 1 #i

NOIP2017D1T3逛公园——哎呦!

#include<cstdio> #include<cstring> #define MXN 100001 #define MXM 200001 #define MXK 51 int afst[MXM],anxt[MXM],av[MXM],aw[MXM]; int bfst[MXM],bnxt[MXM],bv[MXM],bw[MXM]; int dis[MXN],queue[10*MXN]; int f[MXN][MXK],vis[MXN][MXK]; int n,m,k,p,t,

【比赛】NOIP2017 逛公园

考试的时候灵光一闪,瞬间推出DP方程,但是不知道怎么判-1,然后?然后就炸了. 后来发现,我只要把拓扑和DP分开,中间加一个判断,就AC了,可惜. 看这道题,我们首先来想有哪些情况是-1:只要有零环在满足题目要求的路径上,那么这条路径就可以不停地走,于是就-1了. 如何判有没有零环呢? 机械化地两遍不同方向的SPFA,就知道某个点在不在最短路上,以此建一个最短路图,在最短路图上找零环.于是就拓扑啦.稍加判断就解决了整个题目最关键的-1. 接下来就是DP了,设f[i][j]表示走到i点,走过路程已

[NOIP2017] 逛公园

[NOIP2017] 逛公园 题目大意: 给定一张图,询问长度 不超过1到n的最短路长度加k 的1到n的路径 有多少条. 数据范围: 点数\(n \le 10^5\) ,边数\(m \le 2*10^5\) 题目解法 两个月后再看也不是太难,自己就能独立思考出来. 首先是判-1的问题,显然能产生-1的只有0环. 所以把0环都找出来, 然后检查一下\(dis[\)\(1\),环\(]\) + \(dis[\)环,\(n]\) 是否小于等于 \(dis[1,n]+K\)即可. 如果不是无限路径的话,

P3953 NOIP2017 d1t3 逛公园

题目描述 策策同学特别喜欢逛公园.公园可以看成一张NN 个点MM 条边构成的有向图,且没有 自环和重边.其中1号点是公园的入口,NN 号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花的时间. 策策每天都会去逛公园,他总是从1号点进去,从NN 号点出来. 策策喜欢新鲜的事物,它不希望有两天逛公园的路线完全一样,同时策策还是一个 特别热爱学习的好孩子,它不希望每天在逛公园这件事上花费太多的时间.如果1号点 到NN 号点的最短路长为dd ,那么策策只会喜欢长度不超过d + Kd+K