题目链接:https://www.luogu.org/problemnew/show/P3953

题外话:感觉2017年神题好多。。这还不是最神的一道,真在考场上我也就写个最短路计数暴力了。现在在大佬们的帮助下算是理解了些。

方便起见,均设路径为 u—>v 权值为w

首先,看到这个题,我们想到的是最短路计数。

最短路计数的转移是什么?

$ if(dis[v] == dis[u] + e[i].len) ans[e[i].to] = (ans[e[i].to] + ans[now]); $

$ if(dis[e[i].to] > dis[now] + e[i].len) ans[e[i].to] = ans[now]; $

注:ans[i]表示起点到i点的最短路径数

20分代码如下:

(SPFA又暴毙了?)

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 200010;
int dis[maxn], ans[maxn], n, m, k, T, p;
bool vis[maxn];
struct edge{
int next, to, len;
}e[maxn<<2];
int head[maxn], cnt;
queue<int> q;
void add(int u, int v, int w)
{
e[++cnt].len = w;
e[cnt].next = head[u];
e[cnt].to = v;
head[u] = cnt;
}
int Ans;
void SPFA()
{
while(!q.empty())
{
int now = q.front(); q.pop();
vis[now] = 0;
for(int i = head[now]; i != -1; i = e[i].next)
{
if(dis[e[i].to] == dis[now] + e[i].len)
{
ans[e[i].to] = (ans[e[i].to] + ans[now]);
if(!vis[e[i].to])
{
q.push(e[i].to);
vis[e[i].to]=true;
}
}
if(dis[e[i].to] > dis[now] + e[i].len)
{
dis[e[i].to] = dis[now] + e[i].len;
ans[e[i].to] = ans[now];
if(!vis[e[i].to] /*&& ans[e[i].to]*/)
{
q.push(e[i].to);
vis[e[i].to] = 1;
}
}
}
if(now==n) Ans+=ans[now];
ans[now]=0;
}
}
int main()
{
scanf("%d",&T);
while(T--)
{
memset(head, -1, sizeof(head));
memset(dis, 127, sizeof(dis));
memset(vis, 0, sizeof(vis));
memset(ans, 0, sizeof(ans));
while(!q.empty()) q.pop();
scanf("%d%d%d%d",&n,&m,&k,&p);
for(int i = 1; i <= m; i++)
{
int u, v, w;
scanf("%d%d%d",&u,&v,&w);
add(u, v, w);
}
dis[1] = 0;
vis[1] = 1;
ans[1] = 1;
q.push(1);
SPFA();
printf("%d\n",Ans);
Ans=0;
}
}

带劲!

接着看到题目要求计算<=k的路径数,我们考虑是不是可以在ans这玩意后再开一维?

我们设f[i][j]表示当前第i个点,与到n的最短路相差j的方案数。然后沿前驱递归回去计算。

于是有转移方程:

\(f[u][j] = Σf[v][dis[u]+j-w-dis[v]]\)

u是当前点,v是它的前驱,w是它与它前驱的边的权值,j是相差为j,dis[x]表示1—>x的最短路

这个方程我们可以用类似前缀和的想法解释:

当前的dis[u] + j可以理解成我目前到点N可以消耗的路径长度,而dis[v] + w便是如果v点转移到u上去所需要消耗的路径长度,相减若大于等于0,这说明我们是可以从v点过来的,那么这样一直到点1,直到不能再有可以消耗的差值,我们就说明找到了一条从1—>n差值为j的路径。所以初始的状态为f[1][0] = 1。

最后相差为j的答案就会存储在dp[n][j]里。

所以我们要建一个反着的图,为了方便我们枚举前驱。

于是:

for(i = 1; i <= k; i++)

ans += f[n][i];

这时候..考虑怎么判断0环?

如果有0环的话,我的dp方程转移会被转移多次,所以我们用一个used判断是不是被转移多次就好了。

下面用记忆化搜索实现

code:

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 200010;
int dis[maxn], ans, n, m, mod, k, flag, T, f[maxn][51], used[maxn][51];
bool vis[maxn];
struct edge{
int to, next, len;
}e1[maxn<<2], e2[maxn<<2];//正图1 反图2
int head1[maxn], head2[maxn], cnt1, cnt2;
void add1(int u, int v, int w)
{
e1[++cnt1].len = w;
e1[cnt1].next = head1[u];
e1[cnt1].to = v;
head1[u] = cnt1;
}
void add2(int u, int v, int w)
{
e2[++cnt2].len = w;
e2[cnt2].next = head2[u];
e2[cnt2].to = v;
head2[u] = cnt2;
}
//----------------------------------
void SPFA()
{
queue<int> q;
memset(dis, 64, sizeof(dis));
memset(vis, 0, sizeof(vis));
dis[1] = 0, vis[1] = 1;
q.push(1);
while(!q.empty())
{
int now = q.front(); q.pop();
vis[now] = 0;
for(int i = head1[now]; i != -1; i = e1[i].next)
{
if(dis[e1[i].to] > dis[now] + e1[i].len)
{
dis[e1[i].to] = dis[now] + e1[i].len;
if(!vis[e1[i].to])
{
q.push(e1[i].to);
vis[e1[i].to] = 1;
}
}
}
}
}
//----------------------------------
int dfs(int u, int k)
{
if(~f[u][k]) return f[u][k];
used[u][k] = 1;
f[u][k] = 0;
for(int i = head2[u]; i != -1; i = e2[i].next)
{
int v = e2[i].to, w = e2[i].len;
int t = dis[u] + k - w - dis[v];
if(t < 0) continue;
if(used[v][t]) flag = 1;
f[u][k] = (f[u][k] + dfs(v, t)) % mod;
}
used[u][k] = 0;
return f[u][k];
}
//----------------------------------
void init()
{
memset(f, -1, sizeof(f));
memset(used, 0, sizeof(used));
memset(e1, 0, sizeof(e1));
memset(e2, 0, sizeof(e2));
memset(head1, -1, sizeof(head1));
memset(head2, -1, sizeof(head2));
cnt1 = 0, cnt2 = 0, ans = 0, flag = 0;
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d%d",&n,&m,&k,&mod);
init();
for(int i = 1; i <= m; i++)
{
int u, v, w;
scanf("%d%d%d",&u, &v, &w);
add1(u, v, w);
add2(v, u, w);
}
SPFA();
f[1][0] = 1;
for(int i = 0; i <= k; i++)
ans = (ans + dfs(n, i))%mod;
dfs(n, k+1);
if(flag) {printf("-1\n"); continue;}
printf("%d\n",ans);
}
return 0;
}

(SPFA没有死!)

【luogu P3953 逛公园】 题解的更多相关文章

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

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

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

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

  3. Luogu P3953 逛公园

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

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

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

  5. NOIP2017 逛公园 题解报告 【最短路 + 拓扑序 + dp】

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

  6. luogu 3953 逛公园

    noip2017 D1T3 逛公园 某zz选手看到数据范围直接就最短路计数了,结果写错了爆零 题目大意: N个点M条边构成的有向图,且没有自环和重边.其中1号点是起点,N号点是公园的终点,每条边有一个 ...

  7. [NOIP2017]逛公园 题解

    我连D1T3都不会我联赛完蛋了 题目描述 策策同学特别喜欢逛公园.公园可以看成一张 N 个点 M 条边构成的有向图,且没有 自环和重边.其中1号点是公园的入口, N 号点是公园的出口,每条边有一个非负 ...

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

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

  9. 洛谷P3953逛公园

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

随机推荐

  1. SQL Server 2008各版本介绍区别(包含企业版 开发者版 标准版 Web版 工作组版 Express版 Compact版)

    SQL Server 2008分为SQL Server 2008企业版.标准版.工作组版.Web版.开发者版.Express版.Compact 3.5版,其功能和作用也各不相同,其中SQL Serve ...

  2. RFC1939 POP3协议 中文版 (转载)

      1.简介 对于在网络上的比较小的结点,支持消息传输系统(MTS)是不实际的.例如,一台工作站可能不具有充足的资源允许SMTP服务器和相当的本地邮件传送系统保持序驻留,并持续运行.同样的,将一台个人 ...

  3. ARP原理和欺骗

    ARP--在TCP/IP协议栈中,最不安全的协议莫过于ARP了,我们经常听到的网络扫描,内网***,流量欺骗等等,他们基本上都与ARP有关系,甚至可以说,他们的底层都是基于ARP实现的.但是ARP的是 ...

  4. 洛谷 P2469 [SDOI2010]星际竞速 解题报告

    题目描述 10年一度的银河系赛车大赛又要开始了.作为全银河最盛大的活动之一,夺得这个项目的冠军无疑是很多人的梦想,来自杰森座α星的悠悠也是其中之一. 赛车大赛的赛场由N颗行星和M条双向星际航路构成,其 ...

  5. HTML5 Web存储 页面间进行传值

    在实际使用过程中,经常会遇到需要在页面间进行传值的情况,最初设想一定需要后端才能进行数据的存储和读取,或者在本地使用一个cookie进行保存,直到了解到HTML5 Web存储 使用HTML5的新特性可 ...

  6. css3+javascript实现翻页幻灯片

    先上效果图 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <tit ...

  7. css 关闭按钮实现

    通过css的伪元素:before,:after以及transform: rotate(45deg);旋转来实现(支持IE9及其以上版本) <div class="close" ...

  8. Java基础之引用(String,char[],Integer)总结于牛客网的专项练习题

    1.String的引用: 下列代码执行后的结果为: public class Test { public static void main(String[] args) { StringBuffer ...

  9. baidu地图api , 地理位置转 经纬度接口

    更多可以查看    http://lbsyun.baidu.com/index.php?title=webapi/guide/webservice-geocoding 地理编码示例: 以下是关于地理编 ...

  10. 微服务架构之spring cloud gateway

    Spring Cloud Gateway是spring cloud中起着非常重要的作用,是终端调用服务的入口,同时也是项目中每个服务对外暴露的统一口径,我们可以在网关中实现路径映射.权限验证.负载均衡 ...