Luogu 3953[NOIP2017] 逛公园 堆优化dijkstra + 记忆化搜索
题解
首先肯定是要求出单源最短路的,我用了堆优化dijikstra ,复杂度 mlogm,值得拥有!(只不过我在定义优先队列时把greater 打成了 less调了好久
然后我们就求出了$i$到源点的最短距离$dis_i$
定义一个数组 $f_{i, k}$表示从源点到节点$i$的距离比$dis_i$大$k$的路径数,另外一个数组$sch_{i,k}$ 记录某条路径上 该状态是否存在, 若在某条路径上出现了第二次, 并且$k <= K$,则可判断有符合条件的$0$环
对于要求的$f_{i, k}$ 需要求出它所有的前驱状态(也就是拓扑), 设它的某一前驱为 $f_{u,t}$则$t = dis_i + k - dis_u - w$ ,$w$为边权。
由于$t + dis_u + w = k + dis_i$, 所以$t - k = dis_i -dis_u - w$, 因为$dis_i$是到 $i$ 的最短距离, 则显然 $dis_i <= dis_u + w$, 得出$t <= k$, 也就是某个点前驱的 $k$值会不断减小,只需要$k >= 0$ 即可进行转移。
有转移方程 : $f_{i, k} = \sum f_{u,t}$ $t >= 0$。 边界为 $f_{1, 0} = 1$。但是若设$f_{1, 0} = 1$为边界状态的话,对于经过 点 $1$ 的0环就无法判断了。
所以我把边界定为$f_{0,0} = 1$,再从 $1 - > 0$连一条 记忆化搜索时需要记录前驱的反向,边权为0, 这样就可以解决$0$环的问题。
最后的答案$ans = \sum\limits_{k=0}^Kf_{n,k}$。
总时间复杂度为$O(MlogM + KN)$
代码
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<queue>
#include<vector>
#define rd read()
using namespace std;
typedef pair<int,int> P;//用pair来存储dis 与点, 第一个关键字优先排序 const int N = 1e5 + 1e4; int n, m, K, T, head[N], tot, dis[N], f[N][], mod;
vector<int>q[N], w[N];//记录前驱的反向边 bool vis[N], sch[N][], flag = true; priority_queue<P ,vector<P>, greater<P> > pq;//优先队列 struct edge {
int u, v, c, nxt;
}e[N << ]; int read() {
int X = , p = ; char c = getchar();
for(; c > '' || c < ''; c = getchar() ) if( c == '-' ) p = -;
for(; c >= '' && c <= ''; c = getchar() ) X = X * + c - '';
return X * p;
} void add( int u, int v, int c ) {
e[++tot].u = u;
e[tot].v = v;
e[tot].c = c;
e[tot].nxt = head[u];
head[u] = tot;
} void dij() {//求单源最短路
memset( vis, , sizeof(vis));
memset( dis, 0x3f, sizeof(dis));
pq.push(P(, ));
dis[] = ;
for( P u, v; !pq.empty(); ) {
u = pq.top(); pq.pop();
int x = u.second;
if(vis[x]) continue;
vis[x] = ;
for( int i = head[x], nt; i; i = e[i].nxt ) if( dis[nt = e[i].v] > dis[x] + e[i].c && !vis[nt] ) {
dis[nt] = dis[x] + e[i].c;
pq.push(P(dis[nt], nt));
}
}
} int dp( int u, int k ) {//记忆化搜索
if( u == && k == ) return ;
if( f[u][k] != - ) return f[u][k];
f[u][k] = ;
sch[u][k] = ;
for( int i = ; i < (int)q[u].size(); ++i ) {
int nt = q[u][i], t = dis[u] + k - dis[nt] - w[u][i];
if(t < ) continue;
if( sch[nt][t] ) flag = false;//记录路径上的状态
f[u][k] = (f[u][k] + dp(nt, t)) % mod;
}
sch[u][k] = ;//回溯
return f[u][k];
} int main()
{
T = rd;
for(; T; T-- ) {
flag = true;
memset(f, -, sizeof(f));
memset(head, , sizeof(head));
memset(sch, , sizeof(sch));
tot = ;
for( int i = ; i <= n; ++i ) q[i].clear(), w[i].clear();
n = rd; m = rd; K = rd; mod = rd;
for( int i = ; i <= m; ++i ) {
int u = rd, v = rd, c = rd;
add(u, v, c);
q[v].push_back(u);
w[v].push_back(c);
}
q[].push_back();
w[].push_back();
dij();
dis[] = ;
int ans = ;
for( int i = ; i <= K; ++i ) ans = (ans + dp(n, i)) % mod;
if(!flag) printf("-1\n");
else printf("%d\n", ans);
}
}
Luogu 3953[NOIP2017] 逛公园 堆优化dijkstra + 记忆化搜索的更多相关文章
- [NOIP2017] 逛公园 (最短路,动态规划&记忆化搜索)
题目链接 Solution 我只会60分暴力... 正解是 DP. 状态定义: \(f[i][j]\) 代表 \(1\) 到 \(i\) 比最短路长 \(j\) 的方案数. 那么很显然最后答案也就是 ...
- HDU1142 (Dijkstra+记忆化搜索)
A Walk Through the Forest Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Jav ...
- luogu3953 [NOIp2017]逛公园 (tarjan+dijkstra+记忆化搜索)
先跑一边dijkstra算出从1到i的最短距离dis[i] 然后建反向边 从n开始记忆化搜索,(p,k)表示1到p的距离=dis[p]+k的方案数 答案就是$\sum\limits_{i=0}^{k} ...
- 洛谷3953 (NOIp2017) 逛公园——记忆化搜索+用栈判0环
题目:https://www.luogu.org/problemnew/show/P3953 因为K只有50,所以想到用dp[ cr ][ j ]表示在点cr.比最短路多走了 j 的方案数.(看了TJ ...
- HDU 1142 A Walk Through the Forest(Dijkstra+记忆化搜索)
题意:看样子很多人都把这题目看错了,以为是求最短路的条数.真正的意思是:假设 A和B 是相连的,当前在 A 处, 如果 A 到终点的最短距离大于 B 到终点的最短距离,则可以从 A 通往 B 处,问满 ...
- Luogu P2149 [SDOI2009]Elaxia的路线(最短路+记忆化搜索)
P2149 [SDOI2009]Elaxia的路线 题意 题目描述 最近,\(Elaxia\)和\(w**\)的关系特别好,他们很想整天在一起,但是大学的学习太紧张了,他们必须合理地安排两个人在一起的 ...
- Luogu P3953 [NOIP2017]逛公园
题目 首先我们跑出从\(1\)出发的最短路\(d1\)和反图上从\(n\)出发的最短路\(dn\). 然后我们处理出长度不超过\(d1_n+k\)的最短路边集,给它拓扑排序. 如果存在环,那么这个环一 ...
- 堆优化dijkstra
单源最短路径 题目链接:https://www.luogu.org/problemnew/show/P4779 直到做了这个题才发现我之前写的堆优化dijkstra一直是错的.. 这个堆优化其实很容易 ...
- Luogu P3953 逛公园(最短路+记忆化搜索)
P3953 逛公园 题面 题目描述 策策同学特别喜欢逛公园.公园可以看成一张 \(N\) 个点 \(M\) 条边构成的有向图,且没有自环和重边.其中 \(1\) 号点是公园的入口,\(N\) 号点是公 ...
随机推荐
- 学习MongoDB 一:MongoDB 入门(安装与配置)
一.简介 MongoDB一种非关系型数据库(NoSql),是一种强大.灵活.可扩展的数据存储方式,因为MongoDB是文档模型,自由灵活很高,可以让你在开发过程中畅顺无比,对于大数据量.高并发.弱事务 ...
- CYQ.Data 轻量数据层之路 使用篇二曲 MAction 数据查询(十三)----002
原文链接:https://blog.csdn.net/cyq1162/article/details/53303390 前言说明: 本篇继续上一篇内容,本节介绍所有相关查询的使用. 主要内容提要: 1 ...
- pdb调试工具
调试--pdb pdb是基于命令行的调试工具,非常类似gnu的gdb(调试c/c++). 命令 简写命令 作用 break b 设置断点 continue c 继续执行程序 list l 查看当前行的 ...
- [Dart] Flutter开发中的几个常用函数
几个Flutter开发中的常用函数 /** 返回当前时间戳 */ static int currentTimeMillis() { return new DateTime.now().millisec ...
- HTML5 Canvas ( 绘制一轮弯月, 星空中的弯月 )
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- maven项目中的报错问题——Dynamic Web Module 3.0 requires Java 1.6 or newer.
转自:http://www.cnblogs.com/beppezhang/p/5919221.html maven项目中的报错问题——Dynamic Web Module 3.0 requires J ...
- 23.OGNL与ValueStack(VS)-调用普通类的构造方法
转自:https://wenku.baidu.com/view/84fa86ae360cba1aa911da02.html 建立一个新的类:Student,在此省略代码. 然后在loginSuc.js ...
- Python之reduce函数
描述 reduce() 函数会对参数序列中元素进行累积. 函数将一个数据集合(链表,元组等)中的所有数据进行下列操作:用传给reduce中的函数 function(有两个参数)先对集合中的第 1.2 ...
- 转载:canal数据库同步redis
ref: http://blog.csdn.net/tb3039450/article/details/53928351
- mongodb基础学习13-聚集aggregate操作
aggregate可以用的操作与sql的对应关系 下面来看具体操作例子: 分组求和: 求总记录数 商品价格大于50记录分组求和 商品价格大于50且分组记录大于2的分组记录条件 分组库存数,并按库存排序 ...