题目描述

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

策策每天都会去逛公园,他总是从1号点进去,从NNN号点出来。

策策喜欢新鲜的事物,它不希望有两天逛公园的路线完全一样,同时策策还是一个 特别热爱学习的好孩子,它不希望每天在逛公园这件事上花费太多的时间。如果1号点 到NNN号点的最短路长为ddd,那么策策只会喜欢长度不超过d+Kd
+ Kd+K的路线。

策策同学想知道总共有多少条满足条件的路线,你能帮帮它吗?

为避免输出过大,答案对PPP取模。

如果有无穷多条合法的路线,请输出−1。

输入输出格式

输入格式:

第一行包含一个整数 TTT,
代表数据组数。

接下来TTT组数据,对于每组数据:
第一行包含四个整数 N,M,K,PN,M,K,PN,M,K,P,每两个整数之间用一个空格隔开。

接下来MMM行,每行三个整数ai,bi,cia_i,b_i,c_iai​,bi​,ci​,代表编号为ai,bia_i,b_iai​,bi​的点之间有一条权值为
cic_ici​的有向边,每两个整数之间用一个空格隔开。

输出格式:

输出文件包含 TTT
行,每行一个整数代表答案。

输入输出样例

输入样例#1:
复制

  1. 2
  2. 5 7 2 10
  3. 1 2 1
  4. 2 4 0
  5. 4 5 2
  6. 2 3 2
  7. 3 4 1
  8. 3 5 2
  9. 1 5 3
  10. 2 2 0 10
  11. 1 2 0
  12. 2 1 0
输出样例#1:
复制

  1. 3
  2. -1

说明

【样例解释1】

对于第一组数据,最短路为 3。 1 – 5, 1 – 2 – 4 – 5, 1 – 2 – 3 – 5 为 3 条合法路径。

【测试数据与约定】

对于不同的测试点,我们约定各种参数的规模不会超过如下

测试点编号   TTT    NNN    MMM    KKK    是否有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≤P≤109,1≤ai,bi≤N,0≤ci≤10001
\le P \le 10^9,1 \le a_i,b_i \le N ,0 \le c_i \le 10001≤P≤109,1≤ai​,bi​≤N,0≤ci​≤1000。

数据保证:至少存在一条合法的路线。

题解

考场上想到了dp,甚至写出了转移方程,但是由于时间紧迫没有进一步想得出转移顺序,最后交了最短路。。。QAQ
我们看K最大50,考虑一个关于K的dp

设f[u][j]表示到达u路径比到达u的最短路长j的方案数
那么对于一个f[u][j],u的所有边u -> v,有转移方程f[v][d[u] + j + w - d[v]] += f[u][j]
因为当前从1到v的路经长L = 1到u的路经长d[u] + j 加上 边权w,减去d[v]就得到比最短路多的部分
只要这个值不大于K,都可以转移

考虑转移顺序。
由最短路我们有d[v] <= d[u] + w
也就是说d[u] + w - d[v] + j >= j
由于等号的存在我们会产生同状态的转移,这个时候就要考虑转移的顺序
有两种情况:
1、沿着最短路转移
2、沿着0边转移
我们只要确保这两种情况中最靠前的节点先转移就好了
1、对于最短路,最靠前就是d最小的,按d排个序就好了
2、而0边呢,最靠前先转移你想到了什么?
对,拓扑排序:
我们单独用0边建图,跑一次拓扑排序给每个0点标上拓扑序

这样子我们对所有的点双关键字排序,第一关键字最短路,第二关键字拓扑序,因为只有0边两端存在d相等的情况,所以其他的点拓扑序赋什么值都无所谓
最后我们按照排序的顺序写状态转移就A啦~
附上我丑丑的代码:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<queue>
  4. #include<cstring>
  5. #include<algorithm>
  6. #define LL long long int
  7. #define REP(i,n) for (int i = 1; i <= (n); i++)
  8. #define fo(i,x,y) for (int i = (x); i <= (y); i++)
  9. #define Redge(u) for (int k = head[u]; k != -1; k = edge[k].next)
  10. using namespace std;
  11. const int maxn = 200005,maxm = 400005,maxk = 60,INF = 1000000000;
  12. inline int read(){
  13. int out = 0,flag = 1;char c = getchar();
  14. while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
  15. while (c >= 48 && c <= 57) {out = out * 10 + c - 48; c = getchar();}
  16. return out * flag;
  17. }
  18. int N,M,K,P;
  19. int dS[maxn],dT[maxn],f[maxn][maxk],inde[maxn],id[maxn];
  20. bool vis[maxn],zer[maxn];
  21. int head[maxn],nedge = 0,h[maxn],ne = 0;
  22. struct EDGE{int to,w,next;}edge[maxm],e[maxm];
  23. inline void build(int u,int v,int w){
  24. edge[nedge] = (EDGE) {v,w,head[u]}; head[u] = nedge++;
  25. e[ne] = (EDGE) {u,w,h[v]}; h[v] = ne++;
  26. if (!w) zer[u] = zer[v] = true,inde[v]++;
  27. }
  28. struct node{int u,d;};
  29. inline bool operator <(const node& a,const node& b){return a.d > b.d;}
  30. struct Node{int d,ti;}E[maxn];
  31. inline bool cmp(const int& a,const int& b){return E[a].d == E[b].d ? E[a].ti < E[b].ti : E[a].d < E[b].d;}
  32. void dijkstraS(){
  33. REP(i,N) dS[i] = INF,vis[i] = false;
  34. priority_queue<node> q;
  35. dS[1] = 0;
  36. q.push((node){1,dS[1]});
  37. node u; int to;
  38. while (!q.empty()){
  39. u = q.top();
  40. q.pop();
  41. if (vis[u.u]) continue;
  42. vis[u.u] = true;
  43. Redge(u.u)
  44. if (!vis[to = edge[k].to]){
  45. if (dS[to] >= dS[u.u] + edge[k].w){
  46. dS[to] = dS[u.u] + edge[k].w;
  47. q.push((node){to,dS[to]});
  48. }
  49. }
  50. }
  51. }
  52. void dijkstraT(){
  53. REP(i,N) dT[i] = INF,vis[i] = false;
  54. priority_queue<node> q;
  55. dT[N] = 0;
  56. q.push((node){N,dT[N]});
  57. node u; int to;
  58. while (!q.empty()){
  59. u = q.top();
  60. q.pop();
  61. if (vis[u.u]) continue;
  62. vis[u.u] = true;
  63. for (int k = h[u.u]; k != -1; k = e[k].next)
  64. if (!vis[to = e[k].to] && dT[to] > dT[u.u] + e[k].w){
  65. dT[to] = dT[u.u] + e[k].w;
  66. q.push((node){to,dT[to]});
  67. }
  68. }
  69. }
  70. bool tuopu(){
  71. queue<int> q;
  72. REP(i,N){
  73. id[i] = i;
  74. E[i].d = dS[i]; E[i].ti = 0;
  75. if (zer[i] && !inde[i]){
  76. q.push(i);
  77. }
  78. }
  79. int u,to,cnt = 0;
  80. while (!q.empty()){
  81. u = q.front();
  82. q.pop();
  83. E[u].ti = ++cnt;
  84. Redge(u) if (!edge[k].w){
  85. inde[to = edge[k].to]--;
  86. if (!inde[to]) q.push(to);
  87. }
  88. }
  89. REP(i,N) if (zer[i] && inde[i] && dS[i] + dT[i] <= dT[1] + K) {printf("-1\n");return false;}
  90. sort(id + 1,id + 1 + N,cmp);
  91. //REP(i,N) cout<<id[i]<<' ';cout<<endl;
  92. return true;
  93. }
  94. void solve(){
  95. dijkstraS();
  96. dijkstraT();
  97. //REP(i,N) cout<<f[i][0]<<' ';cout<<endl;
  98. if(!tuopu()) return;
  99. f[1][0] = 1;
  100. for (int j = 0; j <= K; j++)
  101. for (int i = 1; i <= N; i++){
  102. int u = id[i];
  103. Redge(u){
  104. int v = edge[k].to;
  105. if (dS[u] + j + edge[k].w - dS[v] <= K){
  106. f[v][dS[u] + j + edge[k].w - dS[v]] = (f[v][dS[u] + j + edge[k].w - dS[v]] + f[u][j]) % P;
  107. }
  108. }
  109. }
  110. int ans = 0;
  111. for (int i = 0; i <= K; i++) ans = (ans + f[N][i]) % P;
  112. printf("%d\n",ans);
  113. }
  114. void init(){
  115. int a,b,c;
  116. memset(f,0,sizeof(f));
  117. N = read(); M = read(); K = read(); P = read();
  118. ne = nedge = 0;
  119. REP(i,N) head[i] = h[i] = -1,inde[i] = 0,zer[i] = false;
  120. while (M--) {a = read(); b = read(); c = read(); build(a,b,c);}
  121. }
  122. int main()
  123. {
  124. int T = read();
  125. while (T--){
  126. init();
  127. solve();
  128. }
  129. return 0;
  130. }


NOIP2017 逛公园 题解报告 【最短路 + 拓扑序 + dp】的更多相关文章

  1. 【NOIP2017】逛公园 拆点最短路+拓扑(记忆化搜索

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

  2. [NOIP2017]逛公园 题解

    我连D1T3都不会我联赛完蛋了 题目描述 策策同学特别喜欢逛公园.公园可以看成一张 N 个点 M 条边构成的有向图,且没有 自环和重边.其中1号点是公园的入口, N 号点是公园的出口,每条边有一个非负 ...

  3. [NOIP2017] 逛公园 解题报告(DP)

    我很不想说 在我的AC代码上我打了表,但实在没有办法了.莫名的8,9个点RE.然而即便是打表...也花了我很久. 这大概是NOIP2017最难的题了,为了让不懂的人更容易理解,这篇题解会比较详细 我的 ...

  4. [NOIP2017]逛公园 最短路+拓扑排序+dp

    题目描述 给出一张 $n$ 个点 $m$ 条边的有向图,边权为非负整数.求满足路径长度小于等于 $1$ 到 $n$ 最短路 $+k$ 的 $1$ 到 $n$ 的路径条数模 $p$ ,如果有无数条则输出 ...

  5. [NOIP2017]逛公园 最短路图 拓扑序DP

    ---题面--- 题解: 挺好的一道题. 首先我们将所有边反向,跑出n到每个点的最短路,然后f[i][j]表示从i号节点出发,路径长比最短路大j的方案数. 观察到,如果图中出现了0环,那么我们可以通过 ...

  6. NOIP2017 Day1 T3 逛公园(最短路+拓扑排序+DP)

    神tm比赛时多清个零就有60了T T 首先跑出1起点和n起点的最短路,因为k只有50,所以可以DP.设f[i][j]表示比最短路多走i的长度,到j的方案数. 我们发现如果在最短路上的和零边会有后向性, ...

  7. Luogu3953 NOIP2017逛公园(最短路+拓扑排序+动态规划)

    跑一遍dij根据最短路DAG进行拓扑排序,按拓扑序dp即可.wa了三发感觉非常凉. #include<iostream> #include<cstdio> #include&l ...

  8. 【题解】NOIP2017逛公园(DP)

    [题解]NOIP2017逛公园(DP) 第一次交挂了27分...我是不是必将惨败了... 考虑这样一种做法,设\(d_i\)表示从该节点到n​节点的最短路径,\(dp(i,k)\)表示从\(i\)节点 ...

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

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

随机推荐

  1. 探寻ASP.NET MVC鲜为人知的奥秘(3):寻找多语言的最佳实践方式

    如果你的网站需要被世界各地的人访问,访问者会使用各种不同的语言和文字书写习惯,那么创建一个支持多语言的网站就是十分必要的了,这一篇文章就讲述怎么快速合理的创建网站对多语言的支持.接下来通过一个实例来讲 ...

  2. 【MySQL安装】MySQL5.6在centos6.4上的安装

    卸载原来安装的mysql 安装从官网下载的mysql rpm包 发现有依赖,需要先安装libaio包和libnuma包 再装mysql就可以了 安装客户端 安装完成后,启动mysql 但是发现用没有m ...

  3. C#课后小作业

    有关C#基础的练手 跟大家一起分享下 1.让用户输入一个100以内的数 打印1-100之间所有的数,用户输入的数除外 2.让用户输入一个100以内的数 打印1-这个数之间所有的数的和 3.使用一个fo ...

  4. json简单操作

    通过内置的json模块对json数据进行编码 1.对数据进行编码(dumps) import json #使用dumps将python数据结构转换为json data = { , "name ...

  5. unittest,requests——接口测试脚本及报告

    用unittest管理两个利用requests模块,做百度搜索的简单接口测试用例,之后自动输出报告 # encoding=utf-8import requests,unittest,HTMLTestR ...

  6. SQL行列乾坤大挪移

    “生活总是这样,有时候,你需要一个苹果,但别人却给了你一个梨.” 今天dalao邮件里需要添加一张每月累计长长的图,可是,拿到手上的SQL导出数据不符合我最爱的pyecharts的数据输入格式,头大. ...

  7. JSON.stringify处理对象时的问题

    1. JSON.stringify({entry_key: 'test', entry_detail: undefined}) 结果 为 "{"entry_key": & ...

  8. 带你玩转JavaScript中的隐式强制类型转换

    正题开始前我想先抛出一个问题,==和===有什么区别?可能一般人会想,不就是后者除了比较值相等之外还会比较类型是否相等嘛,有什么好问的,谁不知道?!但是这样说还不够准确,两者的真正区别其实是==在比较 ...

  9. Codeforces Round #287 (Div. 2) E. Breaking Good 最短路

    题目链接: http://codeforces.com/problemset/problem/507/E E. Breaking Good time limit per test2 secondsme ...

  10. net项目调试时,读取主干或其他项目代码问题

    最近调试项目的时候出了一个很奇怪的问题. 项目总是去读取另外一个项目的配置文件,甚至执行的代码都是另外一个项目的. 我用vs修改代码时候,甚至修改到了其他项目的代码,生成不报错,很奇怪. 本来怀疑是v ...