传送门

花了一个下午才 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 ) 。有助于卡常

最新文章

  1. Memcache学习整理
  2. 如丝般顺滑地从Windows迁移SQLServer数据库到Linux
  3. 从语言到库到框架,再到API,再到标记最后到DSL语言
  4. Cannot return from outside a function or method
  5. gitlab 用户头像不能显示的问题
  6. Longest Palindromic Substring
  7. Linux的启动过程
  8. mybatis中oracle in&gt;1000的处理
  9. CentOS如何挂载硬盘
  10. 【转】android出现注: 某些输入文件使用或覆盖了已过时的 API。 注: 有关详细信息, 请使用 -Xlint:deprecation 重新编译。 注: 某些输入文件使用了未经检查或不安全的操作。 注
  11. 【译】 AWK教程指南 8处理多行数据
  12. ajax调用webService中的方法
  13. java MessageFormat 应用 和 疑惑
  14. MD5和SHA512Managed ——哈希算法
  15. UVA 674 (入门DP, 14.07.09)
  16. dede 你所上传的软件类型不在许可列表,请更改系统对扩展名限定的配置
  17. 第十三课 CSS外观及样式的应用 css学习3
  18. jQuery的deferred对象解析
  19. How to proof MD5
  20. January 05th, 2018 Week 01st Friday

热门文章

  1. mac上sed -i 执行失败报错
  2. CF830A Office Keys(贪心)
  3. 阿里云mysql安装配置(CentOS 7.3 64)
  4. 北京大学Cousera学习笔记--3-计算导论与C语言基础-第一讲.计算机的基本原理-计算机怎么计算-数的二进制
  5. servlet中如何实现通过Spring实现对象的注入
  6. 统计Oracle一个表空间中各个segment占用的空间大小
  7. jquery.uploadify上传插件HTML5版中文api使用说明
  8. Oracle树查询及相关函数
  9. 通过浏览器F12开发工具快速获取别的网站前端代码的方法
  10. jquery的cookie插件