[NOIP2017]逛公园 最短路图 拓扑序DP
题解:
挺好的一道题。
首先我们将所有边反向,跑出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的更多相关文章
- [十二省联考2019]字符串问题——后缀自动机+parent树优化建图+拓扑序DP+倍增
题目链接: [十二省联考2019]字符串问题 首先考虑最暴力的做法就是对于每个$B$串存一下它是哪些$A$串的前缀,然后按每组支配关系连边,做一遍拓扑序DP即可. 但即使忽略判断前缀的时间,光是连边的 ...
- [NOIP2017]逛公园 最短路+拓扑排序+dp
题目描述 给出一张 $n$ 个点 $m$ 条边的有向图,边权为非负整数.求满足路径长度小于等于 $1$ 到 $n$ 最短路 $+k$ 的 $1$ 到 $n$ 的路径条数模 $p$ ,如果有无数条则输出 ...
- [NOIP2017] 逛公园 (最短路,动态规划&记忆化搜索)
题目链接 Solution 我只会60分暴力... 正解是 DP. 状态定义: \(f[i][j]\) 代表 \(1\) 到 \(i\) 比最短路长 \(j\) 的方案数. 那么很显然最后答案也就是 ...
- Luogu3953 NOIP2017逛公园(最短路+拓扑排序+动态规划)
跑一遍dij根据最短路DAG进行拓扑排序,按拓扑序dp即可.wa了三发感觉非常凉. #include<iostream> #include<cstdio> #include&l ...
- 【比赛】NOIP2017 逛公园
考试的时候灵光一闪,瞬间推出DP方程,但是不知道怎么判-1,然后?然后就炸了. 后来发现,我只要把拓扑和DP分开,中间加一个判断,就AC了,可惜. 看这道题,我们首先来想有哪些情况是-1:只要有零环在 ...
- 【题解】NOIP2017逛公园(DP)
[题解]NOIP2017逛公园(DP) 第一次交挂了27分...我是不是必将惨败了... 考虑这样一种做法,设\(d_i\)表示从该节点到n节点的最短路径,\(dp(i,k)\)表示从\(i\)节点 ...
- [NOIP2017] 逛公园
[NOIP2017] 逛公园 题目大意: 给定一张图,询问长度 不超过1到n的最短路长度加k 的1到n的路径 有多少条. 数据范围: 点数\(n \le 10^5\) ,边数\(m \le 2*10^ ...
- Graph_Master(连通分量_H_Trajan+拓扑序dp)
Graph_Master_连通分量_H 题目描述: 一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:?u,v∈V,满足u→v或v→u,即对于图中任意两点u,v,存在一条 ...
- BZOJ_1916_[Usaco2010 Open]冲浪_分层图+拓扑排序+DP
BZOJ_1916_[Usaco2010 Open]冲浪_分层图+拓扑排序+DP Description 受到秘鲁的马丘比丘的新式水上乐园的启发,Farmer John决定也为奶牛们建 一个水上乐园. ...
随机推荐
- 封装一个CSVHelper
public class CSVHelper { /// <summary> /// CSV转换成DataTable(OleDb数据库访问方式) /// </summary> ...
- RTL8188EUS之MAC地址烧写(使用利尔达模组)
1. 手上有几个RTL8188EUS的wifi模块,打算把台式机装个无线网卡,但是插上之后发现没有MAC,没办法只能自己去找个烧写MAC的软件.RTL8188内部有个eFuse,用来配置之类的.这个e ...
- “地表最贵iPhone”到货,iPhone XS 系列手机等你来测!
WeTest 导读 9月13日,苹果正式发布了全新的 iPhone XS 系列智能手机,备受瞩目的iPhone家族新成员具体又是怎样的呢? 关于iPhone XS系列手机的那些新亮点 大屏.双 ...
- react-native android 初始化问题
最近开始接触rn,官方起手,装了一堆工具,然后启动项目的时候出现了一堆问题,这里针对我遇到的一些问题提供一些解决方案. 本人开发环境mac,在启动ios的时候没啥大问题,可以直接启动,这里提示一点,因 ...
- Selenium 入门到精通系列:三
Selenium 入门到精通系列 PS:Driver_Element 常用方法 例子 #!/usr/bin/env python # -*- coding: utf-8 -*- # @Date : 2 ...
- 收割大厂offer需要具备的条件
转载出处 本人也一直在关注互联网,觉得还是有些了解.互联网要求是越来越高了,竞争的人太多了,不过你不用担心,个人觉得,你到了中层的水平,拿二线offer应该没问题,人多也有人多的好处,我比别人多努力一 ...
- VBA基础之Excel 工作表(Sheet)的操作(二)
二. Excel 工作表(Sheet)的操作1. Excel 添加工作表(Sheet) 方法名 参数 参数值 说明 Add Before 工作表名称 在指定的工作表前面插入新的工作表 After 工作 ...
- vue.js学习之 打包为生产环境后,页面为白色
vue.js学习之 打包为生产环境后,页面为白色 一:配置问题 当我们将项目打包为生产环境后,在dist文件夹下打开index.html,会发现页面为白色. 1:打开config>index.j ...
- Nodejs中关于模块的总结
关于Nodejs中的模块 概念 Nodejs在ECMAScript的基础上扩展并封装了许多高级特性,如文件访问.网络访问等,使得Nodejs成为一个很好的Web开发平台.基于Nodejs这个平台将We ...
- Repair the Wall (贪心)
Long time ago , Kitty lived in a small village. The air was fresh and the scenery was very beautiful ...