Luogu P3953NOIP2017逛公园最短路+拓扑排序+动态规划
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Luogu P3953NOIP2017逛公园最短路+拓扑排序+动态规划相关的知识,希望对你有一定的参考价值。
题目描述
策策同学特别喜欢逛公园。公园可以看成一张NN个点MM条边构成的有向图,且没有 自环和重边。其中1号点是公园的入口,NN号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花的时间。
策策每天都会去逛公园,他总是从1号点进去,从NN号点出来。
策策喜欢新鲜的事物,它不希望有两天逛公园的路线完全一样,同时策策还是一个 特别热爱学习的好孩子,它不希望每天在逛公园这件事上花费太多的时间。如果1号点 到NN号点的最短路长为dd,那么策策只会喜欢长度不超过d + Kd+K的路线。
策策同学想知道总共有多少条满足条件的路线,你能帮帮它吗?
为避免输出过大,答案对PP取模。
如果有无穷多条合法的路线,请输出−1。
输入输出格式
输入格式:
第一行包含一个整数 T, 代表数据组数。
接下来TT组数据,对于每组数据: 第一行包含四个整数 N,M,K,PN,M,K,P,每两个整数之间用一个空格隔开。
接下来MM行,每行三个整数a_i,b_i,c_iai?,bi?,ci?,代表编号为a_i,b_iai?,bi?的点之间有一条权值为 c_ici?的有向边,每两个整数之间用一个空格隔开。
输出格式:
输出文件包含T 行,每行一个整数代表答案。
输入输出样例
说明
【样例解释1】
对于第一组数据,最短路为 3。 1 – 5, 1 – 2 – 4 – 5, 1 – 2 – 3 – 5 为 3 条合法路径。
【测试数据与约定】
对于不同的测试点,我们约定各种参数的规模不会超过如下
测试点编号 | TT | NN | MM | KK | 是否有0边 |
---|---|---|---|---|---|
1 | 5 | 5 | 10 | 0 | 否 |
2 | 5 | 1000 | 2000 | 0 | 否 |
3 | 5 | 1000 | 2000 | 50 | 否 |
4 | 5 | 1000 | 2000 | 50 | 否 |
5 | 5 | 1000 | 2000 | 50 | 否 |
6 | 5 | 1000 | 2000 | 50 | 是 |
7 | 5 | 100000 | 200000 | 0 | 否 |
8 | 3 | 100000 | 200000 | 50 | 否 |
9 | 3 | 100000 | 200000 | 50 | 是 |
10 | 3 | 100000 | 200000 | 50 | 是 |
对于 100%的数据, 1 <= P <= 10^9,1 <= a_i,b_i <= N ,0 <= c_i <= 1000, 1≤P≤109,1≤ai?,bi?≤N , 0≤ci?≤1000。
数据保证:至少存在一条合法的路线。
Solution :
首先从起点和终点的最短路一定是要跑的。
注意到k不大于50, nk的动态规划是可以考虑的。
设起点到第i个点最短路为dis[i] , 这样dp[i][j]表示到第i个点长度为dis[i]+j的路径条数。
考虑到可以同一层转移,显然这种转移边满足dis[x] + len == dis[y]。
那么我么拓扑排序一下确定转移顺序即可。
考虑-1的情况,若有零环且存在通过零环并且长度<=dis[n]+k的路径则答案无限大。
namespace可好写了,写得可长了。
#include <cstdio> #include <iostream> #include <cstring> #include <algorithm> #include <queue> #define travel(x, i) for (int i = fir[x]; i; i = e[i].nxt) using namespace std; namespace fastIO { #define buf_size 1000000 bool error; inline char gc() { static char buf[buf_size + 1], *l = buf, *r = buf; if (l == r) { l = buf; r = buf + fread(buf, 1, buf_size, stdin); if (l == r) {error = 1; return -1;} } return *l ++; } inline bool isnum(char ch) { return (‘0‘ <= ch && ch <= ‘9‘) || ch == ‘-‘; } inline void read(int &x) { char ch; x = 0; while (!isnum(ch = gc()) && !error); if (error) return; do {x = (x << 1) + (x << 3) + ch - ‘0‘;} while (isdigit(ch = gc()) && !error); } #undef buf_size } using namespace fastIO; typedef pair <int, int> pii; const int N = 1e5 + 5; const int M = 2e5 + 5; int n, m, k, mod; int ans[N][52]; namespace Graph1 { struct edge { int nxt, to, len; } e[M]; int fir[N], dis[N], cnt; bool in[N]; inline void clear() {memset(fir, 0, sizeof fir); cnt = 0;} inline void add(int x, int y, int l) { e[++ cnt] = (edge){fir[x], y, l}; fir[x] = cnt; } inline void dijkstra() { memset(dis, -1, sizeof dis); memset(in, 0, sizeof in); priority_queue <pii, vector <pii>, greater <pii> > Q; Q.push(make_pair(0, 1)); dis[1] = 0; pii cur; int x; while (!Q.empty()) { cur = Q.top(); Q.pop(); x = cur.second; if (in[x]) continue; in[x] = 1; travel(x, i) if (!in[e[i].to] && (dis[x] + e[i].len < dis[e[i].to] || dis[e[i].to] == -1)) { dis[e[i].to] = dis[x] + e[i].len; Q.push(make_pair(dis[e[i].to], e[i].to)); } } } } namespace Graph2 { struct edge { int nxt, to, len; } e[M]; int fir[N], dis[N], cnt; bool in[N]; inline void clear() {memset(fir, 0, sizeof fir); cnt = 0;} inline void add(int x, int y, int l) { e[++ cnt] = (edge){fir[x], y, l}; fir[x] = cnt; } inline void dijkstra() { memset(dis, -1, sizeof dis); memset(in, 0, sizeof in); priority_queue <pii, vector <pii>, greater <pii> > Q; Q.push(make_pair(0, n)); dis[n] = 0; pii cur; int x; while (!Q.empty()) { cur = Q.top(); Q.pop(); x = cur.second; if (in[x]) continue; in[x] = 1; travel(x, i) if (!in[e[i].to] && (dis[x] + e[i].len < dis[e[i].to] || dis[e[i].to] == -1)) { dis[e[i].to] = dis[x] + e[i].len; Q.push(make_pair(dis[e[i].to], e[i].to)); } } } } namespace Graph3 { struct edge { int nxt, to; } e[M]; int fir[N], cnt = 0, tot; int ord[N], deg[N]; inline void clear() { memset(fir, 0, sizeof fir); memset(deg, 0, sizeof deg); cnt = 0; } inline void add(int x, int y) { e[++ cnt] = (edge){fir[x], y}; fir[x] = cnt; deg[y] ++; } inline void topo() { queue <int> Q; for (int i = 1; i <= n; i ++) if (!deg[i]) Q.push(i); int x; tot = 0; while (!Q.empty()) { x = Q.front(); Q.pop(); ord[++ tot] = x; travel(x, i) { deg[e[i].to] --; if (!deg[e[i].to]) Q.push(e[i].to); } } } } namespace Graph4 { struct edge { int nxt, to; } e[M]; int fir[N], deg[N], cnt; inline void clear() { memset(fir, 0, sizeof fir); memset(deg, 0, sizeof deg); cnt = 0; } inline void add(int x, int y) { e[++ cnt] = (edge){fir[x], y}; fir[x] = cnt; deg[y] ++; } inline bool topo() { queue <int> Q; for (int i = 1; i <= n; i ++) if (!deg[i]) Q.push(i); int x; while (!Q.empty()) { x = Q.front(); Q.pop(); travel(x, i) { deg[e[i].to] --; if (!deg[e[i].to]) Q.push(e[i].to); } } for (int i = 1; i <= n; i ++) if (deg[i] && Graph1 :: dis[i] + Graph2 :: dis[i] <= Graph1 :: dis[n] + k) return 0; return 1; } } int x[M], y[M], l[M]; inline void modadd(int &x, int y) { if ((x += y) >= mod) x -= mod; } using namespace Graph1; inline void solve(int T) { clear(); Graph2 :: clear(); Graph3 :: clear(); Graph4 :: clear(); read(n); read(m); read(k); read(mod); for (int i = 1; i <= m; i ++) { read(x[i]); read(y[i]); read(l[i]); add(x[i], y[i], l[i]); Graph2 :: add(y[i], x[i], l[i]); } dijkstra(); Graph2 :: dijkstra(); for (int i = 1; i <= m; i ++) { if (dis[x[i]] == -1 || Graph2 :: dis[y[i]] == -1) continue; if (dis[x[i]] + l[i] == dis[y[i]]) Graph3 :: add(x[i], y[i]); if (l[i] == 0) Graph4 :: add(x[i], y[i]); } if (Graph4 :: topo() == 0) return (void)puts("-1"); Graph3 :: topo(); memset(ans, 0, sizeof ans); ans[1][0] = 1; int w, xx, g = Graph3 :: tot; for (int i = 0; i <= k; i ++) { for (int j = 1; j <= g; j ++) { xx = Graph3 :: ord[j]; if (dis[xx] == -1) continue; travel(xx, p) { w = dis[xx] + i + e[p].len - dis[e[p].to]; if (0 <= w && w <= k) modadd(ans[e[p].to][w], ans[xx][i]); } } } int Ans = 0; for (int i = 0; i <= k; i ++) modadd(Ans, ans[n][i]); printf("%d\n", Ans); } int main() { int T; read(T); while (T --) solve(T); return 0; }
以上是关于Luogu P3953NOIP2017逛公园最短路+拓扑排序+动态规划的主要内容,如果未能解决你的问题,请参考以下文章
[luogu P3953] [noip2017 d1t3] 逛公园