[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\)即可。
如果不是无限路径的话,也比较套路了。直接把距离扔到\(DP\)维数中肯定不现实。
所以设\(f[ u ][ d ]\)表示从1到u,长度为\(dis[1,u]\)+\(d\) 的路径有多少条。
我们假设\(u\) --> \(v\),\(d_u = rest'\) , \(d_v = rest\) , 那么有如下关系:
\[rest' + dis[1 , u ] + t[ i ].lg\ \ =\ \ rest + dis[1 , v]\]
转移:\(f[v][rest] = \sum f[ u ][rest']\) , 初值\(f[1][0] = 1\)
细节比较多 , 求 \(0\)环 判 -1 可以用\(Tarjan\)做,只走边权为0的边即可。
然后\(DP\)的时候转移顺序不好处理 , 所以记忆化搜索即可。
实现代码:
注:记忆化搜索倒着搜比较方便就倒着搜了。
#include<bits/stdc++.h>
#define RG register
#define IL inline
#define os 55
#define _ 200005
#define INF 1000000007
using namespace std;
IL int gi(){
RG int data = 0 , m = 1; RG char ch = 0;
while(ch != '-' && (ch < '0' || ch > '9') ) ch = getchar();
if( ch == '-' ) { ch = getchar(); m = 0; }
while(ch >= '0' && ch <= '9'){data = (data << 1) + (data << 3) + (ch ^ 48); ch = getchar();}
return ( m ) ? data : -data;
}
int Case,N,M,K,P,zero_res,oo,top,cnt,ans,init[_];
int dis[_][2],dp[_][os],dfn[_],low[_],stk[_],tmp[_],hd[_];
struct Road{int to , next , w ; }t[2*_][ 2 ] ; int head[ _ ][ 2 ];
bool vis[_]; queue<int>Q;
IL void spfa(RG int S , RG int id){
for(RG int i = 1; i <= N; i ++)dis[ i ][id] = INF ;
vis[ S ] = true; Q.push( S ) ; dis[ S ][id] = 0;
while(!Q.empty()){
RG int u = Q.front(); Q.pop();
for(RG int i = head[ u ][id] ; i ; i = t[ i ][id].next){
RG int v = t[ i ][id].to ;
if(dis[ v ][id] > dis[ u ][id] + t[ i ][id].w){
dis[ v ][id] = dis[ u ][id] + t[ i ][id].w ;
if(! vis[ v ] ) Q.push( v ) , vis[ v ] = true;
}
}vis[ u ] = false;
}return;
}
IL void Tarjan( RG int u ){
stk[ ++ top ] = u;
dfn[ u ] = low[ u ] = ++ oo ; init[ u ] = true;
for(RG int i = head[ u ][0] ; i ; i = t[ i ][0].next){
if(t[ i ][0].w != 0)continue; RG int v = t[ i ][0].to;
if( !dfn[ v ] )
Tarjan( v ) , low[ u ] = min(low[ u ] , low[ v ]) ;
else if(init[ v ])low[ u ] = min(low[ u ] , dfn[ v ]) ;
}
if(low[ u ] == dfn[ u ]){
RG int e , ct = 0;
while(1){
e = stk[ top ] ; top --;
init[e] = false; tmp[ ++ct ] = e;
if(e == u || !top) break;
}
if(ct >= 2)
zero_res = min( dis[ u ][ 0 ] + dis[ u ][ 1 ] , zero_res) ;
}return;
}
IL int DP( RG int u , RG int rest ) {
if( dp[ u ][ rest ] )return dp[ u ][ rest ] ;
for(RG int i = head[ u ][ 1 ] ; i ; i = t[ i ][ 1 ].next){
RG int v = t[ i ][ 1 ].to , d;
d = rest + dis[ u ][ 0 ] - dis[ v ][ 0 ] - t[ i ][ 1 ].w ;
if( d < 0 )continue;
dp[ u ][ rest ] = ( dp[ u ][ rest ] + DP( v , d ) ) % P;
}return dp[ u ][ rest ] ;
}
//v-->u : rest' + dis[v][0] + t[i].w = rest + dis[u][0]
int main(){
freopen("2017park.in" , "r" , stdin);
freopen("2017park.out" , "w" , stdout);
Case = gi();
while(Case -- ){
N = gi(); M = gi(); K = gi(); P = gi();
for(RG int i = 1; i <= N; i ++)head[ i ][ 0 ] = 0;
for(RG int i = 1; i <= N; i ++)head[ i ][ 1 ] = 0;
for(RG int i = 1; i <= N; i ++)dfn[ i ] = low[ i ] = 0;
cnt = 0;
for(RG int i = 1 , u , v , c; i <= M; i ++){
u = gi(); v = gi(); c = gi(); ++ cnt ;
t[ cnt ][ 0 ] = ( Road ) { v , head[ u ][ 0 ] , c } ;
head[ u ][ 0 ] = cnt ;
t[ cnt ][ 1 ] = ( Road ) { u , head[ v ][ 1 ] , c } ;
head[ v ][ 1 ] = cnt ;
//0 : u --> v (oder) // 1 : v --> u (dder)
}
spfa(1 , 0) ; spfa(N , 1) ;
zero_res = INF ;
for(RG int i = 1; i <= N ; i ++) if( ! dfn[ i ] ) Tarjan( i ) ;
if(zero_res <= dis[ N ][ 0 ] + K){ puts("-1") ; continue; }
for(RG int i = 1; i <= N; i ++)
for(RG int j = 0; j <= K; j ++)
dp[ i ][ j ] = 0;
dp[ 1 ][ 0 ] = 1; ans = 0;
for(RG int delta = 0; delta <= K; delta ++)
ans = ( ans + DP( N , delta ) ) % P;
cout << ans << endl;
}return 0;
}
[NOIP2017] 逛公园的更多相关文章
- 【比赛】NOIP2017 逛公园
考试的时候灵光一闪,瞬间推出DP方程,但是不知道怎么判-1,然后?然后就炸了. 后来发现,我只要把拓扑和DP分开,中间加一个判断,就AC了,可惜. 看这道题,我们首先来想有哪些情况是-1:只要有零环在 ...
- 【题解】NOIP2017逛公园(DP)
[题解]NOIP2017逛公园(DP) 第一次交挂了27分...我是不是必将惨败了... 考虑这样一种做法,设\(d_i\)表示从该节点到n节点的最短路径,\(dp(i,k)\)表示从\(i\)节点 ...
- NOIP2017逛公园(dp+最短路)
策策同学特别喜欢逛公园.公园可以看成一张N个点M条边构成的有向图,且没有 自环和重边.其中1号点是公园的入口,N号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花的时间. 策策每天都会 ...
- NOIP2017 逛公园 题解报告 【最短路 + 拓扑序 + dp】
题目描述 策策同学特别喜欢逛公园.公园可以看成一张NNN个点MMM条边构成的有向图,且没有 自环和重边.其中1号点是公园的入口,NNN号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花 ...
- [NOIP2017]逛公园 题解
我连D1T3都不会我联赛完蛋了 题目描述 策策同学特别喜欢逛公园.公园可以看成一张 N 个点 M 条边构成的有向图,且没有 自环和重边.其中1号点是公园的入口, N 号点是公园的出口,每条边有一个非负 ...
- [NOIP2017] 逛公园 解题报告(DP)
我很不想说 在我的AC代码上我打了表,但实在没有办法了.莫名的8,9个点RE.然而即便是打表...也花了我很久. 这大概是NOIP2017最难的题了,为了让不懂的人更容易理解,这篇题解会比较详细 我的 ...
- [NOIP2017] 逛公园 【最短路】【强连通分量】
题目分析: 首先考虑无数条的情况.出现这种情况一定是一条合法路径经过了$ 0 $环中的点.那么预先判出$ 0 $环中的点和其与$ 1 $和$ n $的距离.加起来若离最短路径不超过$ k $则输出$ ...
- luogu3953 [NOIp2017]逛公园 (tarjan+dijkstra+记忆化搜索)
先跑一边dijkstra算出从1到i的最短距离dis[i] 然后建反向边 从n开始记忆化搜索,(p,k)表示1到p的距离=dis[p]+k的方案数 答案就是$\sum\limits_{i=0}^{k} ...
- noip2017逛公园
题解: 之前知道正解并没有写过.. #include <bits/stdc++.h> using namespace std; #define rint register int #def ...
随机推荐
- 随机手机号和身份证号码(python)
在使用selenium2 python自动化过程中,用户添加的时候程序设置的手机号和身份证号码是唯一的,这方面python代码可以实现,以下是调试成功,可以实现的. 具体代码如下 身份证需要下载dis ...
- linux下卸载已安装的软件
1.先查询该软件是否安装,是否存在 rpm -qa | grep -i teamview 2.根据一中的结果(软件包名称),执行如下命令 rpm -e [软件包名]
- Python自动化--语言基础4--模块、文件读写、异常
模块1.什么是模块?可以理解为一个py文件其实就是一个模块.比如xiami.py就是一个模块,想引入使用就在代码里写import xiami即可2.模块首先从当前目录查询,如果没有再按path顺序逐一 ...
- bzoj 1307/1318 玩具 线段树+记录时间戳
玩具 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 743 Solved: 404[Submit][Status][Discuss] Descrip ...
- 如何写出测不出bug的测试用例
我们写测试用例的目的是为了能够整理思路,把要测试的地方列出来,做为知识的积淀,用例可以交给其他测试人员执行,或者是跟需求提出者进行讨论,对用例进行补充和修改. 理论上用例写的越多,越容易发现bug.但 ...
- Tensorflow中实现BN为什么需要加入这个额外依赖?见CS231N作业源码
batch normalization in tensorflow requires this extra dependency 为什么加上这两句? extra_update_ops = tf.get ...
- 剑指offer随练
合并两个排序的链表 题目描述 输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则. 思路:使用递归的方法,合并头节点,然后对剩下的链表接着合并头节点,直到合并完 ...
- Ajax检测用户名是否已经注册
程序功能 当用户名输入完成(即用户名输入框失去焦点),利用Ajax检测用户名是否已经注册! 实现过程 利用Ajax向CheckUserServlet发送请求,判断该用户名是否可用.这里只是为了演示Aj ...
- uva1347 经典dp
详细的思路书上面有,有一点要强调的是题意容易理解错:必须严格向右或则向左移动,不能到了第3个点又回到第2个点.否则这个状态方程是不成立的,变成了NP难问题 状态方程: dp[i][j]=min(dp[ ...
- 《清华梦的粉碎》by王垠
清华梦的诞生 小时候,妈妈给我一个梦.她指着一个大哥哥的照片对我说,这是爸爸的学生,他考上了清华大学,他是我们中学的骄傲.长大后,你也要进入清华大学读书,为我们家争光.我不知道清华是什么样子,但是我 ...