传送门

花了一个下午才 A 的毒瘤题

思路:

  这题需要建两个图,一个正向图,一个反向图。

  先在正向图上跑一遍 dijkstar ,计算出每个点到 点1 的最短路径 。

  然后在反向图上开始记忆化搜索:

  - 和动规一样,先定义 f [ i ][ j ] 表示:从 点 1 到 点 i 的距离为 dis [ i ] + j 的方案数。(初始值要为负,不然判断 0环 的时候会出错)

  - 对于每一条反向边(u,v,w)都有 f [ u ][ d ] = ∑ f [ u ][(dis[ u ] + d)-(dis[ v ] + w)] 。

    设 lck_dis = (dis[ u ] + d)-(dis[ v ] + w)。

    仔细分析会发现 lck_dis 就表示 点 u 到 起点 1 的距离。

    这类似于 最短路计数 的转移,因为建的是反向边,所以要从后往前转移。

  - 为了判断是否有毒瘤的 0边,需要开一个布尔数组 flag [ i ][ j ] 判断 dp 值是否被经过 2 次,且没有被确定,那么一定是有 0边 导致的自环。

Code:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<string>
#include<stack>
#include<vector>
#include<queue>
#include<deque>
#include<map>
#include<set>
using namespace std;
#define INF 0x3f
const int maxn=1e5+;
inline int read()
{
int xs=,kr=;
char ls;
ls=getchar();
while(!isdigit(ls))
{
if(ls=='-')
kr=-;
ls=getchar();
}
while(isdigit(ls))
{
xs=(xs<<)+(xs<<)+(ls^);
ls=getchar();
}
return xs*kr;
}
int T,n,m,k,p;
long long ans;
int head_dij[maxn],head_dfs[maxn],dis[maxn],f[maxn][];
bool bo,vis[maxn],flag[maxn][];
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q;
struct hh
{
int nex,to,w;
} t_dij[maxn<<],t_dfs[maxn<<];
inline void clear()
{
bo=false;
memset(head_dij,,sizeof(head_dij));
memset(head_dfs,,sizeof(head_dfs));
memset(dis,INF,sizeof(dis));
memset(vis,false,sizeof(vis));
memset(f,-,sizeof(f));
}
inline void add(int cnt,int nex,int to,int w)
{
t_dij[cnt].to=to; t_dij[cnt].w=w;
t_dij[cnt].nex=head_dij[nex]; head_dij[nex]=cnt; t_dfs[cnt].to=nex; t_dfs[cnt].w=w;
t_dfs[cnt].nex=head_dfs[to]; head_dfs[to]=cnt;
}
inline void dijkstra(int s)
{
dis[s]=;
q.push(make_pair(,s));
while (!q.empty())
{
int u=q.top().second;
q.pop();
if (vis[u]) continue;
vis[u]=true;
for (int i=head_dij[u];i;i=t_dij[i].nex)
{
int v=t_dij[i].to,w=t_dij[i].w;
if (!vis[v]&&dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
q.push(make_pair(dis[v],v));
}
}
}
}
int dfs(int u,int d)
{
if (flag[u][d])
{
bo=true;
return ;
}
if (~f[u][d]) return f[u][d];
flag[u][d]=true;f[u][d]=;
for (int i=head_dfs[u];i;i=t_dfs[i].nex)
{
if(bo) break;
int v=t_dfs[i].to,w=t_dfs[i].w;
int lck_dis=(dis[u]+d)-(w+dis[v]);
if (lck_dis>=) f[u][d]=(f[u][d]+dfs(v,lck_dis))%p;
}
flag[u][d]=false;
if (u==&&!d) return ++f[u][d];
return f[u][d];
}
int x,y,z;
int main()
{
//freopen("park.in","r",stdin);
//freopen("park.out","w",stdout);
T=read();
while (T--)
{
clear();
n=read();m=read();k=read();p=read();
for(int i=;i<=m;i++)
{
x=read();y=read();z=read();
add(i,x,y,z);
}
dijkstra();
ans=dfs(n,)%p;
for(int i=;i<=k;i++)
{
if(bo) break;
ans=(ans+dfs(n,i))%p;
}
if(bo) printf("-1\n");
else printf("%lld\n",ans);
}
return ;
}

注:在 dfs 中,if ( ~ f [ u ][ d ] ) ⇔ if ( f [ u ][ d ] ≥ 0 ) 。有助于卡常

P3953 逛公园的更多相关文章

  1. P3953 逛公园(dp,最短路)

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

  2. Luogu P3953 逛公园(最短路+记忆化搜索)

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

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

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

  4. LOJ P3953 逛公园 NOIP dp 最短路 拓扑排序

    https://www.luogu.org/problemnew/show/P3953 开o2过了不开o2re一个点...写法如题 顺便一提这道题在我校oj是a不了的因为我校土豆服务器速度奇慢1s时限 ...

  5. 【luogu P3953 逛公园】 题解

    题目链接:https://www.luogu.org/problemnew/show/P3953 题外话:感觉2017年神题好多..这还不是最神的一道,真在考场上我也就写个最短路计数暴力了.现在在大佬 ...

  6. 洛谷P3953逛公园

    题目 作为\(NOIp2017D1T3\) 这个题还是很良心的,至少相对于\(NOIp2018\)来说,希望\(NOIp2019\)不会这么坑吧. 这个题可以作为记忆化搜索的进阶题了,做这个题的方法也 ...

  7. 洛谷 P3953 逛公园

    题目链接 思路 首先没有0边,且k为0的情况就是最短路计数. 如果k不为0,看到k<=50,想到dp. 设f[u][i]表示到达u点比最短路多走i的路径数,转移到v点. f[u][i]+=f[v ...

  8. 洛谷P3953 逛公园(NOIP2017)(最短/长路,拓扑排序,动态规划)

    洛谷题目传送门 又是一年联赛季.NOIP2017至此收官了. 这个其实是比较套路的图论DP了,但是细节有点恶心. 先求出\(1\)到所有点的最短路\(d1\),和所有点到\(n\)的最短路\(dn\) ...

  9. Luogu P3953 逛公园

    不管怎么说,这都是一道十分神仙的NOIp题 你可以说它狗,但不可以否认它就是NOIp的难度 首先这道题很显然是道图论题还是一道图论三合一(最短路+拓扑+图上DP) 先考虑最短路,我们分别以\(1\)和 ...

随机推荐

  1. raise error

    raise TypeError("参数q_type 错误 ")

  2. CentOS7安装vsftpd3.0.2、以及虚拟用户配置

    vsftpd(very secure ftp daemon)是一款运行在Linux操作系统上的FTP服务程序,不仅完全开源而且免费,还具有很高的安全性.传输速度,以及支持虚拟用户验证. vsftpd ...

  3. java深拷贝的实现

    深拷贝实现的工具类 package com.Utils; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStre ...

  4. 2019-oo-第二次总结

    这一单元是关于模拟电梯运行,考验多线程的一个单元,难度由简入入深,从多线程单部电梯,到优化,再到多线程多部电梯,难度一次次的提高. 一.多线程单部电梯(傻瓜调度) 1.设计策略 这一次我只额外的开了一 ...

  5. 基于FPGA的序列检测器10010

    最近在学习状态机,用状态机实现序列检测器10010. 思路如下: 1. S0代表当前数据0,如果检测到0就停在S0,如果检测到1就进入S1. 2. S1代表当前数据1,如果检测到0就进入S2,如果检测 ...

  6. 学Python的感受

    这门课程已经上了两周了,虽然还没学到什么实质上的东西,只是做了几道题,但是我也感受到了Python的魅力.我感觉这门课真的很有用,比如老师所说的网络爬虫,我对这个非常感兴趣.再说说老师的教学方式,理论 ...

  7. [批处理]使用IncrediBuilder加速自动化测试过程

    背景 1.目前单机全部跑完一次测试的时间大概是5个小时 2.7个虚拟机进行分布式执行的时间大概为3.5小时(包含复制.收集日志的时间) 预期 期望通过IncrediBuilder的虚拟进程的功能在单机 ...

  8. Jenkins连接Window服务器,上传jar并启动

    https://blog.csdn.net/achenyuan/article/details/81181347

  9. c++算法实现(一) - 递归和初始化

    递归 写递归函数经常出错,提醒自己两个规则: 1. 一般来说递归函数中不应该出现for.while之类的循环语句, 因为递归就是循环的另外一种实现: 2. 注意基线条件,具体参考<算法图解> ...

  10. 2018-2019-3 网络对抗技术 20165305 Exp3 免杀原理与实践

    1.实验内容及步骤 1.1 正确使用msf编码器,msfvenom生成如jar之类的其他文件,veil-evasion,加壳工具,使用shellcode编程 将做实验二时生成的后门文件用virusto ...