[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)$ 。
//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]逛公园的更多相关文章
- NOIP 2017 逛公园 记忆化搜索 最短路 好题
题目描述: 策策同学特别喜欢逛公园.公园可以看成一张N个点MM条边构成的有向图,且没有 自环和重边.其中1号点是公园的入口,N号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花的时间. ...
- NOIP 2017 逛公园 - 动态规划 - 最短路
题目传送门 传送门 题目大意 给定一个$n$个点$m$条边的带权有向图,问从$1$到$n$的距离不超过最短路长度$K$的路径数. 跑一遍最短路. 一个点拆$K + 1$个点,变成一个DAG上路径计数问 ...
- 洛谷 P3953 [ NOIP 2017 ] 逛公园 —— 最短路DP
题目:https://www.luogu.org/problemnew/show/P3953 主要是看题解...还是觉得好难想啊... dfs DP,剩余容量的损耗是边权减去两点最短路差值...表示对 ...
- NOIP 2017 逛公园 题解
题面 这道题是一道不错的计数类DP: 首先我们一定要跑一遍dijkstra来求得每个点到1号点的最短路: 注意题干,题中并没有说所有点都可以到达n好点,只说了存在一条1号点到n号点的路径:所以我们在反 ...
- 【NOIP 2017】逛公园
Description 策策同学特别喜欢逛公园.公园可以看成一张N个点M条边构成的有向图,且没有 自环和重边.其中1号点是公园的入口,N号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要 ...
- [NOIP 2017 day1]逛公园
题目描述 策策同学特别喜欢逛公园. 公园可以看成一张 N 个点 M 条边构成的有向图,且没有自环和重边.其中 1 号点是公园的入口, N 号点是公园的出口,每条边有一个非负权值,代表策策经过这条边所要 ...
- 逛公园[NOIP2017 D2 T3](dp+spfa)
题目描述 策策同学特别喜欢逛公园. 公园可以看成一张 \(N\)个点\(M\) 条边构成的有向图,且没有自环和重边.其中 1号点是公园的入口,N号点是公园的出口,每条边有一个非负权值,代表策策经过这条 ...
- NOIP2017 Day1 T3 逛公园
NOIP2017 Day1 T3 更好的阅读体验 题目描述 策策同学特别喜欢逛公园.公园可以看成一张\(N\)个点\(M\)条边构成的有向图,且没有 自环和重边.其中1号点是公园的入口,\(N\)号点 ...
- [Luogu P3953] 逛公园 (最短路+拓扑排序+DP)
题面 传送门:https://www.luogu.org/problemnew/show/P3953 Solution 这是一道神题 首先,我们不妨想一下K=0,即求最短路方案数的部分分. 我们很容易 ...
随机推荐
- bug终结者 团队作业第二周
bug终结者 团队作业第二周 我们小组选取游戏"开心消消乐",回答问题: 1. 此类软件是什么时候开始出现的, 这些软件是怎么说服你(陌生人)成为他们的用户的? 他们的目标都是盈利 ...
- Windows下编译SDL
Windows下编译SDL的理由我就不多说了,无论用VS来编译或调试SDL库都是很方便的.而且SDL源代码中也包含了VC工程,你所要做的只是解压VC工程,进行适当的配置,然后编译.调试. 编译SDL大 ...
- 使用SecureCRTP 连接生产环境的web服务器和数据库服务器
一.使用SecureCRTP 连接生产环境的web服务器 首先,需要知道以下参数信息: 1.web服务器的ip地址 2.服务器的端口号 3.会话连接的用户名和密码 4.服务器的用户名 ...
- 《javascript设计模式与开发实践》阅读笔记(16)—— 状态模式
状态模式 会区分事物内部的状态,事物内部状态的改变往往会带来事物的行为改变.比如电灯的开关是开还是关,在外界的表现就完全不同. 电灯例子 按照常规思路,实现一个电灯就是构造一个电灯类,然后指定一下它的 ...
- .net mvc 利用分部视图局部刷新.
页面利于$.Ajax: $(function(){ $("#btnpartview").click(function () { var model = []; model.push ...
- C语言Linix服务器网络爬虫项目(一)项目初衷和网络爬虫概述
一.项目初衷和爬虫概述 1.项目初衷 本人的大学毕设就是linux上用c写的一个爬虫,现在我想把它完善起来,让他像一个企业级别的项目.为了重复发明轮子来学习轮子的原理,我们不使用第三方框架(这里是说的 ...
- python识别验证码——PIL,pytesser,pytesseract的安装
1.使用Python识别验证码需要安装Python的图像处理模块(PIL.pytesser.pytesseract) (安装过程需要pip,在我的Python中已经安装pip了,pip的安装就不在赘述 ...
- nodejs 全局变量
1.全局对象 所有模块都可以调用 1)global:表示Node所在的全局环境,类似于浏览器中的window对象. 2)process:指向Node内置的process模块,允许开发者与当前进程互动. ...
- python 内置函数之lambda-filter-reduce-apply-map
(1)lambda lambda是Python中一个很有用的语法,它允许你快速定义单行最小函数.类似于C语言中的宏,可以用在任何需要函数的地方. 基本语法如下: 函数名 = lambda args1, ...
- ios开发常识(1)开发语言和参考资料
学iphone开发用的语言是object-c,object-c和c++,java还是有很大区别,如果你坚持学习iphone开发的话可以不学c++,java,直接学习这个语言,先入为主,可能觉得这个语言 ...