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)$ 。
 //Is is made by Awson on 2017.12.16
#include <set>
#include <map>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <cstdio>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
#define getnode(x, y) (((x)-1)*(k+1)+(y))
using namespace std;
const int N = ;
const int M = ;
const int K = ;
int read() {
int sum = ;
char ch = getchar();
while (ch < '' || ch > '') ch = getchar();
while (ch >= '' && ch <= '') sum = (sum<<)+(sum<<)+ch-'', ch = getchar();
return sum;
} int n, m, p, k, u[M+], v[M+], c[M+];
struct tt {
int to, next, cost;
}edge[(M*K<<)+];
int path[(N*K<<)+], top, path2[N+];
int dist[N+][];
bool vis[N+];
int Q[(N*K<<)+], head, tail;
int ans[(N*K<<)+], in[(N*K<<)+];
void add(int u, int v, int c) {
edge[++top].to = v;
edge[top].cost = c;
edge[top].next = path[u];
path[u] = top;
}
void add2(int u, int v, int c) {
edge[++top].to = v;
edge[top].cost = c;
edge[top].next = path2[u];
path2[u] = top;
}
void SPFA(int u, int t) {
dist[u][t] = ;
memset(vis, , sizeof(vis)); vis[u] = ;
Q[head = tail = ] = u; tail++;
while (head < tail) {
int u = Q[head]; ++head, vis[u] = ;
for (int i = path2[u]; i; i = edge[i].next)
if (dist[edge[i].to][t] > dist[u][t]+edge[i].cost) {
dist[edge[i].to][t] = dist[u][t]+edge[i].cost;
if (!vis[edge[i].to]) {
vis[edge[i].to] = ; Q[tail] = edge[i].to, ++tail;
}
}
}
}
void topsort() {
memset(ans, , sizeof(ans)); ans[] = ;
int MAX = getnode(n, k), sum = ; head = tail = ;
for (int i = ; i <= MAX; ++i) if (!in[i]) Q[tail] = i, ++tail;
while (head < tail) {
int u = Q[head]; ++head, ++sum;
for (int i = path[u]; i; i = edge[i].next) {
--in[edge[i].to]; ans[edge[i].to] = (ans[edge[i].to]+ans[u])%p;
if (!in[edge[i].to]) Q[tail] = edge[i].to, ++tail;
}
}
if (MAX+ != sum) {
printf("-1\n"); return;
}
int cnt = ;
for (int i = ; i <= k; i++)
cnt = (cnt+ans[getnode(n, i)])%p;
printf("%d\n", cnt);
} void work() {
n = read(), m = read(), k = read(), p = read();
memset(dist, /, sizeof(dist));
memset(path2, top = , sizeof(path2));
for (int i = ; i <= m; i++) {
u[i] = read(), v[i] = read(), c[i] = read();
add2(u[i], v[i], c[i]);
}
SPFA(, );
memset(path2, top = , sizeof(path2));
for (int i = ; i <= m; i++) add2(v[i], u[i], c[i]);
SPFA(n, );
memset(path, top = , sizeof(path));
memset(in, , sizeof(in));
for (int i = ; i <= m; i++) {
int a = u[i], b = v[i], d = c[i];
if (d <= dist[b][]-dist[a][]+k) {
int delta = d-(dist[b][]-dist[a][]), basea = getnode(a, ), baseb = getnode(b, delta);
for (int j = ; j <= k-delta && dist[a][]+dist[b][]+d+j <= dist[n][]+k; j++) {
add(basea+j, baseb+j, );
in[baseb+j]++;
}
}
}
topsort();
}
int main() {
int t; cin >> t;
while (t--) work();
return ;
}

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

  1. NOIP 2017 逛公园 记忆化搜索 最短路 好题

    题目描述: 策策同学特别喜欢逛公园.公园可以看成一张N个点MM条边构成的有向图,且没有 自环和重边.其中1号点是公园的入口,N号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花的时间. ...

  2. NOIP 2017 逛公园 - 动态规划 - 最短路

    题目传送门 传送门 题目大意 给定一个$n$个点$m$条边的带权有向图,问从$1$到$n$的距离不超过最短路长度$K$的路径数. 跑一遍最短路. 一个点拆$K + 1$个点,变成一个DAG上路径计数问 ...

  3. 洛谷 P3953 [ NOIP 2017 ] 逛公园 —— 最短路DP

    题目:https://www.luogu.org/problemnew/show/P3953 主要是看题解...还是觉得好难想啊... dfs DP,剩余容量的损耗是边权减去两点最短路差值...表示对 ...

  4. NOIP 2017 逛公园 题解

    题面 这道题是一道不错的计数类DP: 首先我们一定要跑一遍dijkstra来求得每个点到1号点的最短路: 注意题干,题中并没有说所有点都可以到达n好点,只说了存在一条1号点到n号点的路径:所以我们在反 ...

  5. 【NOIP 2017】逛公园

    Description 策策同学特别喜欢逛公园.公园可以看成一张N个点M条边构成的有向图,且没有 自环和重边.其中1号点是公园的入口,N号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要 ...

  6. [NOIP 2017 day1]逛公园

    题目描述 策策同学特别喜欢逛公园. 公园可以看成一张 N 个点 M 条边构成的有向图,且没有自环和重边.其中 1 号点是公园的入口, N 号点是公园的出口,每条边有一个非负权值,代表策策经过这条边所要 ...

  7. 逛公园[NOIP2017 D2 T3](dp+spfa)

    题目描述 策策同学特别喜欢逛公园. 公园可以看成一张 \(N\)个点\(M\) 条边构成的有向图,且没有自环和重边.其中 1号点是公园的入口,N号点是公园的出口,每条边有一个非负权值,代表策策经过这条 ...

  8. NOIP2017 Day1 T3 逛公园

    NOIP2017 Day1 T3 更好的阅读体验 题目描述 策策同学特别喜欢逛公园.公园可以看成一张\(N\)个点\(M\)条边构成的有向图,且没有 自环和重边.其中1号点是公园的入口,\(N\)号点 ...

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

    题面 传送门:https://www.luogu.org/problemnew/show/P3953 Solution 这是一道神题 首先,我们不妨想一下K=0,即求最短路方案数的部分分. 我们很容易 ...

随机推荐

  1. java并发包——阻塞队列BlockingQueue及源码分析

    一.摘要 BlockingQueue通常用于一个线程在生产对象,而另外一个线程在消费这些对象的场景,例如在线程池中,当运行的线程数目大于核心的线程数目时候,经常就会把新来的线程对象放到Blocking ...

  2. java虚拟机的内存分配与回收机制

    分为4个方面来介绍内存分配与回收,分别是内存是如何分配的.哪些内存需要回收.在什么情况下执行回收.如何监控和优化GC机制. java GC(Garbage Collction)垃圾回收机制,是java ...

  3. 结对作业NO.2

    结对NO.2 1. 引言 1.1 项目地址 github 生成的一组好数据 1.2 项目简介 按照老师给的项目要求:"编码实现一个部门与学生的智能匹配的程序".由于数据需要自己生成 ...

  4. 《Language Implementation Patterns》之 数据聚合符号表

    本章学习一种新的作用域,叫做数据聚合作用域(data aggregate scope),和其他作用域一样包含符号,并在scope tree里面占据一个位置. 区别在于:作用域之外的代码能够通过一种特殊 ...

  5. java利用iTextWorker生成pdf

    使用itext生成pdf,在linux环境下,中文全部失踪,因为itext要在linux下支持中文字体需要引入itext-asian, 并添加一个字体类. public static class Pd ...

  6. 火车头采集器对接织梦cms图集发布时, 采集网上图片超时的解决方法

    背景介绍: 火车头采集器对接织梦cms图片集发布时, 对于多张(超过30张)大图片时, 经常会出现图集发布超时的情况.  问题分析: 因为php对于资源的处理有默认的超时时间30秒, 而我尝试了好多方 ...

  7. JAVA_SE基础——64.StringBuffer类 ①

     字符串特点:字符串是常量:它们的值在创建之后不能更改 字符串的内容一旦发生了变化,那么马上会创建一个新的对象. 注意:字符串的内容不适宜频繁修改,因为一旦修改马上就会创建一个新的对象. publ ...

  8. babel基本用法

    babel-cli babel-cli是本地使用编译js文件 1.安装: cnpm i babel-cli babel-preset-env -D 2.配置packjson: "script ...

  9. JMeter入门(03)多台JMeter联合测试

    一.配置各个节点 1.配置jmeter.properties # Remote Hosts - comma delimited#remote_hosts=localhost:1099,localhos ...

  10. C#微信公众号开发——错误一

    一.网站发布后,运行报如下错误