~~~题面~~~

题解:

  挺好的一道题。

  首先我们将所有边反向,跑出n到每个点的最短路,然后f[i][j]表示从i号节点出发,路径长比最短路大j的方案数。

  观察到,如果图中出现了0环,那么我们可以通过在环上走无数次来获得无数条不同路径,因此这就无解了。

  如果没有0环,当且仅当这张图的最短路图是一个DAG(可以画图思考一下),因为如果没有0环,而最短路图中出现了环,那么意味着你可以无数次以最短路到达同一个点,而不增加路径长,这显然是不可能的,同理,如果有0环,那么最短路图中就会出现环。

  因此我们判断不合法只需要对图进行一遍拓扑排序,如果不能将所有点都加入队列的话,就是出现了环,那么就输出-1.

  否则的话我们就按照拓扑序DP。

  从感性的角度上来说,,,我们需要先获取离终点近的DP值才能更新里终点远的DP值。所以要按拓扑序DP(具体证明之类的我也不会)

  DP的时候要先枚举比最短路长多少,因为DP时要通过原图转移,所以一个离终点近的点也可能会利用到离终点远的点,而DP的转移显然要依赖于用来更新其他点的值要 在被需要之前 更新完。

  所以先枚举点是不对的,因为这样没有明确的需要与被需要关系,也可以认为是在一个环上互相转移了。

  但是观察比最短路长多少这个条件,它是有明确的需要与被需要关系的,对于f[i][j]而言,j大的要利用j小的转移,j小的不可能用j大的转移,因为没有负边,所以这就避免了“环”的出现,于是可以保证DP转移合法。

 #include<bits/stdc++.h>
using namespace std;
#define R register int
#define AC 100100
#define ac 202000
#define LL long long int n, m, k, p, T;
int in[AC], dis[AC];
int q1[ac], head, tail;
LL f[AC][];
bool z[AC]; struct node{
int dis, x;
}; struct cmp{
bool operator() (node a, node b){
return a.dis > b.dis;
}
}; priority_queue<node, vector<node>, cmp> q; struct road{
int Head[AC], date[ac], Next[ac], len[ac], tot; inline void init()
{
memset(Head, , sizeof(Head));
tot = ;
} inline void add(int f, int w, int S){
date[++tot] = w, Next[tot] = Head[f], Head[f] = tot, len[tot] = S;
}
}E1, E2, E3; int read()
{
int x = ;char c = getchar();
while(c > '' || c < '') c = getchar();
while(c >= '' && c <= '') x = x * + c - '', c = getchar();
return x;
} inline void up(LL &a, LL b)
{
a += b;
if(a > p) a -= p;
} void pre()
{
int a, b, c;
E1.init(), E2.init(), E3.init();
memset(f, , sizeof(f));
// memset(in, 0, sizeof(in));
memset(z, , sizeof(z));
head = tail = ;
n = read(), m = read(), k = read(), p = read();
for(R i = ; i <= m; i ++)
{
a = read(), b = read(), c = read();
E1.add(b, a, c), E3.add(a, b, c);
}
memset(dis, , sizeof(dis));
dis[n] = , q.push((node){, n});
} void spfa()
{
int x, now;
while(!q.empty())
{
x = q.top().x, q.pop();
while(z[x] && !q.empty()) x = q.top().x, q.pop();
if(z[x]) break;
z[x] = true;
for(R i = E1.Head[x]; i; i = E1.Next[i])
{
now = E1.date[i];
if(dis[now] > dis[x] + E1.len[i])
{
dis[now] = dis[x] + E1.len[i];
q.push((node){dis[now], now});
}
}
}
} void tsort()
{
int x, now;
while(head < tail)
{
x = q1[++head];
for(R i = E2.Head[x]; i; i = E2.Next[i])
{
now = E2.date[i], --in[now];
if(!in[now]) q1[++tail] = now;
}
}
} void build()
{
int now;
for(R i = ; i <= n; i ++)
{
for(R j = E1.Head[i]; j; j = E1.Next[j])
{
now = E1.date[j];
if(dis[now] == dis[i] + E1.len[j])
E2.add(i, now, E1.len[j]), ++ in[now];
}
//f[i][0] = 1;
}
f[n][] = ;
for(R i = ; i <= n; i ++)
if(!in[i]) q1[++tail] = i;
tsort();
} void getans()
{
if(tail < n)
{
memset(in, , sizeof(in));
printf("-1\n");
return ;
}
int now, x;
for(R i = ; i <= k; i ++)
{
for(R j = ; j <= n; j ++)
{
x = q1[j];
for(R l = E3.Head[x]; l; l = E3.Next[l])
{
now = E3.date[l];
int t = E3.len[l] - dis[x] + dis[now];//获取现在新增的路径长度
if(i - t >= ) up(f[x][i], f[now][i - t]);
}
}
}
LL ans = ;
for(R i = ; i <= k; i ++) up(ans, f[][i]);
printf("%lld\n", ans);
} void work()
{
T = read();
while(T --)
{
pre();
spfa();
build();
getans();
}
} int main()
{
freopen("in.in", "r", stdin);
work();
fclose(stdin);
return ;
}

[NOIP2017]逛公园 最短路图 拓扑序DP的更多相关文章

  1. [十二省联考2019]字符串问题——后缀自动机+parent树优化建图+拓扑序DP+倍增

    题目链接: [十二省联考2019]字符串问题 首先考虑最暴力的做法就是对于每个$B$串存一下它是哪些$A$串的前缀,然后按每组支配关系连边,做一遍拓扑序DP即可. 但即使忽略判断前缀的时间,光是连边的 ...

  2. [NOIP2017]逛公园 最短路+拓扑排序+dp

    题目描述 给出一张 $n$ 个点 $m$ 条边的有向图,边权为非负整数.求满足路径长度小于等于 $1$ 到 $n$ 最短路 $+k$ 的 $1$ 到 $n$ 的路径条数模 $p$ ,如果有无数条则输出 ...

  3. [NOIP2017] 逛公园 (最短路,动态规划&记忆化搜索)

    题目链接 Solution 我只会60分暴力... 正解是 DP. 状态定义: \(f[i][j]\) 代表 \(1\) 到 \(i\) 比最短路长 \(j\) 的方案数. 那么很显然最后答案也就是 ...

  4. Luogu3953 NOIP2017逛公园(最短路+拓扑排序+动态规划)

    跑一遍dij根据最短路DAG进行拓扑排序,按拓扑序dp即可.wa了三发感觉非常凉. #include<iostream> #include<cstdio> #include&l ...

  5. 【比赛】NOIP2017 逛公园

    考试的时候灵光一闪,瞬间推出DP方程,但是不知道怎么判-1,然后?然后就炸了. 后来发现,我只要把拓扑和DP分开,中间加一个判断,就AC了,可惜. 看这道题,我们首先来想有哪些情况是-1:只要有零环在 ...

  6. 【题解】NOIP2017逛公园(DP)

    [题解]NOIP2017逛公园(DP) 第一次交挂了27分...我是不是必将惨败了... 考虑这样一种做法,设\(d_i\)表示从该节点到n​节点的最短路径,\(dp(i,k)\)表示从\(i\)节点 ...

  7. [NOIP2017] 逛公园

    [NOIP2017] 逛公园 题目大意: 给定一张图,询问长度 不超过1到n的最短路长度加k 的1到n的路径 有多少条. 数据范围: 点数\(n \le 10^5\) ,边数\(m \le 2*10^ ...

  8. Graph_Master(连通分量_H_Trajan+拓扑序dp)

    Graph_Master_连通分量_H 题目描述: 一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:?u,v∈V,满足u→v或v→u,即对于图中任意两点u,v,存在一条 ...

  9. BZOJ_1916_[Usaco2010 Open]冲浪_分层图+拓扑排序+DP

    BZOJ_1916_[Usaco2010 Open]冲浪_分层图+拓扑排序+DP Description 受到秘鲁的马丘比丘的新式水上乐园的启发,Farmer John决定也为奶牛们建 一个水上乐园. ...

随机推荐

  1. ruby 技巧 根据函数的返回

    一般语言中,函数必须有返回值,即要带个return关键字.但在ruby中,return不是必须的,如果不写会默认返回最终计算的结果.举例 def add(a,b) # 省去了return a + b ...

  2. dota2交换物品

    改成.bat 因为文件就可以 echo/>>c:/windows/system32/drivers/etc/hostsecho 111.230.82.224 steamcommunity. ...

  3. tensorflow学习一

    1.用图(graph)来表示计算任务 2.用op(opreation)来表示图中的计算节点,图有默认的计算节点,构建图的过程就是在其基础上加节点. 3.用tensor表示每个op的输入输出数据,可以使 ...

  4. JavaWeb——JavaWeb开发入门

    一.基本概念 1.1.WEB开发的相关知识 WEB,在英语中web即表示网页的意思,它用于表示Internet主机上供外界访问的资源. Internet上供外界访问的Web资源分为: 静态web资源( ...

  5. MyEclipse相关用法介绍

    MyEclipse相关用法介绍 ================================================================================ 编辑: ...

  6. 【SpringCloud】 第十篇: 高可用的服务注册中心

    前言: 必需学会SpringBoot基础知识 简介: spring cloud 为开发人员提供了快速构建分布式系统的一些工具,包括配置管理.服务发现.断路器.路由.微代理.事件总线.全局锁.决策竞选. ...

  7. 第六阶段·数据库MySQL及NoSQL实践第1章·章节一MySQL数据库

    01 课程介绍 02 数据库管理系统介绍 03 MySQL安装方式介绍及源码安装 04 MySQL安装后的基本配置 05 MySQL体系结构-服务器.客户端模型 06 MySQL体系结构-实例.连接层 ...

  8. git push origin master 错误解决办法

    一.错误代码如下: error: failed to push some refs to 'https://github.com/wbingithub/drag.git' 二.在网上搜了一下,如下写就 ...

  9. 变量不加 var 声明——掉进坑中,无法自拔!

    整整一下午,都在解决 window.onresize 中方法丢失不执行的问题!姿势固定在电脑前,颈椎病都犯了. 前些日子与大家分享了一下关于 防止jquery $(window).resize()多次 ...

  10. ubuntu server guide 学习笔记

    1. 软件包 1.1. dpkg dpkg -l dpkg -l | grep apache2 dpkg -L ufw dpkg -S /etc/host.conf dpkg -i zip_3.0-4 ...