~~~题面~~~

题解:

  挺好的一道题。

  首先我们将所有边反向,跑出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. 【JUC源码解析】Exchanger

    简介 Exchanger,并发工具类,用于线程间的数据交换. 使用 两个线程,两个缓冲区,一个线程往一个缓冲区里面填数据,另一个线程从另一个缓冲区里面取数据.当填数据的线程将缓冲区填满时,或者取数据的 ...

  2. node-redis使用记录

    redis的高速存取性能让人印象深刻,虽然是分布式存储,但相比本地内存,性能毫不逊色. 之所以能做到这点,是由于redis的“单线程,多路复用IO”,同一时刻只有一个操作在进行. 而且多次建立从red ...

  3. python 水仙花

    #简单def narcissus(): for n in range(100, 1000, 1): a, b, c = n//100, (n//10)%10, (n%100)%10 if a ** 3 ...

  4. Object里面的方法

    object里面有12个方法,没写完,写一些部分代表 toString():输出对象的地址字符串(hashcode码) equals():用的是==,比较的是引用,在有些类里面是重写了这个方法的,重写 ...

  5. 域名、IP地址、URL关系

    域名是个文字形式记录的IP地址 IP地址是计算机在网络中的门牌号! URL是网页地址 例如1: http://zhidao.baidu.com/question/14674128.html 是URL ...

  6. 周期串 (Periodic Strings,UVa455)

    #include<stdio.h> #include<string.h> int main(void) { int n,stlen,i,j; ]; while(scanf(&q ...

  7. 【MySQL解惑笔记】Navicat 无法远程连接MySQL数据库

    安装好Navicat之后远程连接MySQL数据库出现以下报错截图: 出现以上截图怀疑是mysql用户权限不够: GRANT ALL PRIVILEGES ON *.* TO 'root'@'192.1 ...

  8. python读取日志,存入mysql

    1.从 http://www.almhuette-raith.at/apache-log/access.log 下载 1万条日志记录,保存为一个文件,读取文件并解析日志,从日志中提取ip, time_ ...

  9. 论文笔记:Deep Attentive Tracking via Reciprocative Learning

    Deep Attentive Tracking via Reciprocative Learning NIPS18_tracking Type:Tracking-By-Detection 本篇论文地主 ...

  10. python中spilt()函数和os.path.spilt()函数区别

    Python中有split()和os.path.split()两个函数: split():拆分字符串.通过指定分隔符对字符串进行切片,并返回分割后的字符串列表. os.path.split():将文件 ...