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. apache学习教程

    5.apache教程 httpd.conf文件分析 ServerRoot "E:/phpwebenv/PHPTutorial/Apache" #apache软件安装的位置 List ...

  2. java中的异常以及 try catch finally以及finally的执行顺序

    java中的 try.catch.finally及finally执行顺序详解: 1.首相简单介绍一下异常以及异常的运行情况: 在Java中异常的继承主要有两个: Error和Exception 这两个 ...

  3. Docker深入浅出系列教程——Docker简介

    我是架构师张飞洪,钻进浩瀚代码,十年有余,人不堪其累,吾不改其乐.如果你和我的看法不一样,请关注我的头条号,我们一起奇闻共赏,疑义相析. 本节属于入门简介,从三个小方面进行简单介绍Docker. Do ...

  4. C程序设计-----第0次作业

    C程序设计-----第0次作业- 1.翻阅邹欣老师的关于师生关系博客,并回答下列问题,每个问题的答案不少于500字:(50分)- 1)最理想的师生关系是健身教练和学员的关系,在这种师生关系中你期望获得 ...

  5. 《javascript设计模式与开发实践》阅读笔记(12)—— 享元模式

    享元模式 享元(flyweight)模式是一种用于性能优化的模式,"fly"在这里是苍蝇的意思,意为蝇量级.享元模式的核心是运用共享技术来有效支持大量细粒度的对象. 享元模式的核心 ...

  6. PHP截取日期

    date( 'Y-m-d ',strtotime('2017-10-9 12:23:35')) 通过时间格式,获取的是2017-10-9

  7. JAVA_SE基础——61.字符串入门

    public class Demo1 { public static void main(String[] args) { String str1 = "hello"; Strin ...

  8. IIS 配置 FTP 网站

    在 服务器管理器 的 Web服务器IIS 上安装 FTP 服务 在 IIS管理器 添加FTP网站 配置防火墙规则 说明:服务器环境是Windows Server 2008 R2,IIS7.5. 1. ...

  9. JWT

    Web安全通讯之Token与JWT http://blog.csdn.net/wangcantian/article/details/74199762 javaweb多说本地身份说明(JWT)之小白技 ...

  10. 自己造轮子系列之OOM框架AutoMapper

    [前言] OOM框架想必大家在Web开发中是使用频率非常之高的,如果还不甚了解OOM框架,那么我们对OOM框架稍作讲解. OOM顾名思义,Object-Object-Mapping实体间相互转换.常见 ...