DP+图论大毒瘤。

推荐这个博客

先跑两遍最短路,搞掉一些无用点。

然后选出最短路上的边,做拓扑排序。

然后每层DP。

具体看代码。

用到的数组较多,记得清空。

 #include <cstdio>
#include <queue>
#include <cstring>
const int N = ; inline void read(int &x) {
x = ;
char c = getchar();
while(c > '' || c < '') {
c = getchar();
}
while(c <= '' && c >= '') {
x = (x << ) + (x << ) + c - ;
c = getchar();
}
return;
} struct Edge {
int nex, len, v;
}edge[N << ], edge_[N << ]; int top; int n, m, K, MO, f[N][];
int e[N], e_[N], d[N], d_[N];
bool vis[N], use_e[N << ], use_p[N];
int topo[N], in[N], TOPO; inline void add(int x, int y, int z) {
edge[++top].v = y;
edge[top].len = z;
edge[top].nex = e[x];
e[x] = top;
edge_[top].v = x;
edge_[top].len = z;
edge_[top].nex = e_[y];
e_[y] = top;
return;
} inline void SPFA() {
std::queue<int> Q;
memset(vis, , sizeof(vis));
memset(d, 0x3f, sizeof(d));
Q.push();
d[] = ;
vis[] = ;
while(!Q.empty()) {
int x = Q.front();
Q.pop();
vis[x] = ;
for(int i = e[x]; i; i = edge[i].nex) {
int y = edge[i].v;
if(d[y] > d[x] + edge[i].len) {
d[y] = d[x] + edge[i].len;
if(!vis[y]) {
vis[y] = ;
Q.push(y);
}
}
}
}
for(int i = ; i <= n; i++) {
if(d[i] == 0x3f3f3f3f) {
use_p[i] = ;
}
}
return;
} inline void SPFA_() {
std::queue<int> Q;
memset(vis, , sizeof(vis));
memset(d_, 0x3f, sizeof(d_));
Q.push(n);
vis[n] = ;
d_[n] = ;
while(!Q.empty()) {
int x = Q.front();
Q.pop();
vis[x] = ;
for(int i = e_[x]; i; i = edge_[i].nex) {
int y = edge_[i].v;
if(d_[y] > d_[x] + edge_[i].len) {
d_[y] = d_[x] + edge_[i].len;
if(!vis[y]) {
vis[y] = ;
Q.push(y);
}
}
}
}
for(int i = ; i <= n; i++) {
if(d_[i] == 0x3f3f3f3f) {
use_p[i] = ;
}
}
return;
} inline void solve() {
int x, y, z;
read(n);
read(m);
read(K);
read(MO);
top = ;
memset(e, , sizeof(e));
memset(e_, , sizeof(e_));
for(int i = ; i <= m; i++) {
read(x);
read(y);
read(z);
add(x, y, z);
}
memset(use_p, , sizeof(use_p));
//printf("use_p %d \n", use_p[2]);
SPFA_();
SPFA(); memset(use_e, , sizeof(use_e));
memset(in, , sizeof(in));
for(int i = ; i <= m; i++) {
int x = edge_[i].v;
int y = edge[i].v;
if(use_p[x] && use_p[y] && d[x] + edge[i].len == d[y]) {
use_e[i] = ;
in[y]++;
}
} /// topo sort
TOPO = ;
std::queue<int> Q;
for(int i = ; i <= n; i++) {
if(!in[i]) {
Q.push(i);
}
}
while(!Q.empty()) {
int x = Q.front();
Q.pop();
topo[++TOPO] = x;
for(int i = e[x]; i; i = edge[i].nex) {
if(!use_e[i]) {
continue;
}
int y = edge[i].v;
in[y]--;
if(!in[y]) {
Q.push(y);
}
}
}
if(TOPO < n) {
printf("-1\n");
return;
} /// DP
memset(f, , sizeof(f));
f[][] = ;
for(int k = ; k <= K; k++) {
for(int a = ; a <= n; a++) {
int x = topo[a];
if(!use_p[x]) {
continue;
}
for(int i = e[x]; i; i = edge[i].nex) {
int y = edge[i].v;
if(!use_p[y]) {
continue;
}
int temp = d[x] + edge[i].len - d[y] + k;
if(temp > K) {
//printf("temp = %d > K \n", temp);
continue;
}
//printf("f[%d][%d] += f[%d][%d] ", y, temp, x, k);
f[y][temp] += f[x][k];
f[y][temp] %= MO;
//printf("= %d \n", f[y][temp]);
}
}
} int ans = ;
for(int i = ; i <= K; i++) {
ans = (ans + f[n][i]) % MO;
}
printf("%d\n", ans); return;
} int main() {
int T;
read(T);
while(T--) {
solve();
}
return ;
}

AC代码

感觉是我用memset最多的一次了。

有个记忆化搜索的写法,先坑着。

洛谷P3953 逛公园的更多相关文章

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

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

  2. 洛谷P3953逛公园

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

  3. 洛谷 P3953 逛公园

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

  4. 2018.11.01 洛谷P3953 逛公园(最短路+dp)

    传送门 设f[i][j]f[i][j]f[i][j]表示跟最短路差值为iii当前在点jjj的方案数. in[i][j]in[i][j]in[i][j]表示在被选择的集合当中. 大力记忆化搜索就行了. ...

  5. 洛谷P3953 逛公园 [noip2017] 图论+dp

    正解:图论(最短路)+dp(记忆化搜索) 解题报告: 这题真的是个好东西! 做了这题我才发现我的dij一直是错的...但是我以前用dij做的题居然都A了?什么玄学事件啊...我哭了TT 不过其实感觉还 ...

  6. 洛谷P3953 逛公园(dp 拓扑排序)

    题意 题目链接 Sol 去年考NOIP的时候我好像连最短路计数都不会啊qwq.. 首先不难想到一个思路,\(f[i][j]\)表示到第\(i\)个节点,与最短路之差长度为\(j\)的路径的方案数 首先 ...

  7. 洛谷 P3953 逛公园【spfa+记忆化dfs+bfs】

    spfa预处理出最短路数组dis,然后反向建边bfs出ok[u]表示u能到n点 然后发现有0环的话时候有inf解的,先dfs找0环判断即可 然后dfs,设状态f[u][v]为到u点,还可以跑最短路+v ...

  8. 洛谷 P1053 逛公园 解题报告

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

  9. UVA 1400."Ray, Pass me the dishes!" -分治+线段树区间合并(常规操作+维护端点)并输出最优的区间的左右端点-(洛谷 小白逛公园 升级版)

    "Ray, Pass me the dishes!" UVA - 1400 题意就是线段树区间子段最大和,线段树区间合并,但是这道题还要求输出最大和的子段的左右端点.要求字典序最小 ...

随机推荐

  1. Socket异步通信及心跳包同时响应逻辑分析(最后附Demo)。

    有段时间没有更博了,刚好最近在做Socket通信的项目,原理大致内容:[二维码-(加logo)]-->提供主机地址和端口号信息(直接使用[ThoughtWorks.QRCode.dll]比较简单 ...

  2. SQL行转列汇总 (转)

    PIVOT 用于将列值旋转为列名(即行转列),在 SQL Server 2000可以用聚合函数配合CASE语句实现 PIVOT 的一般语法是:PIVOT(聚合函数(列) FOR 列 in (…) )A ...

  3. Sql_join left right

    1.内连接inner join 只返回两张表中所有满足连接条件的行,即使用比较运算符根据每个表中共有的列的值匹配两个表中的行.(inner关键字是可省略的) ①传统的连接写法: 在FROM子句中列出所 ...

  4. BugkuCTF sql注入

    前言 写了这么久的web题,算是把它基础部分都刷完了一遍,以下的几天将持续更新BugkuCTF WEB部分的题解,为了不影响阅读,所以每道题的题解都以单独一篇文章的形式发表,感谢大家一直以来的支持和理 ...

  5. Docker容器学习梳理 - SSH方式登陆容器

    前面几篇已经介绍了Docker基础环境的部署,下面介绍下通过ssh方式登陆Docker容器的操作记录(其实不太建议直接用ssh去连接上容器的想法,虽然可以,但是有很多弊端,而且docker已经提供了容 ...

  6. yum源使用的几个报错小总结 (例如: python2.6.6 下yum不能使用: No module named yum)

    服务器上的yum突然不好使用,使用yum时有如下几个保持,解决方案如下: 1)Error: Cannot retrieve repository metadata (repomd.xml) for r ...

  7. 树莓派 Raspberry Pi 更换国内源

    http://www.shumeipaiba.com/wanpai/jiaocheng/16.html

  8. OneZero第一次随感

    >本人基础薄弱,有幸加入这个团队,甚感欣慰.这是本人第一次尝试写博客,说实话,胆怯.因为能力有限,怕技能匮乏,说不好.但是我知道既然加入这个团队,就要为团队负责.万事开头难,过程也挺难(就我个人 ...

  9. M2事后会议报告

    设想和目标 1. 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? Beta阶段的爬虫需要更稳定.更高效.操作更便捷.在定义中爬取对性能和功能的要求高,典型用户和场景 ...

  10. Linux内核及分析 第二周 操作系统是如何工作的?

    计算机是如何工作的? 存储程序计算机工作模型,计算机系统最最基础性的逻辑结构: 函数调用堆栈,高级语言得以运行的基础,只有机器语言和汇编语言的时候堆栈机制对于计算机来说并不那么重要,但有了高级语言及函 ...