@wsndy-xx
2018-05-05T03:25:13.000000Z
字数 2931
阅读 1557
wsndy
18.05.03
题解策策同学特别喜欢逛公园。公园可以看成一张 个点 每条边构成的有向图,且没有 自环和重边。其中 号点是公园的入口, 号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花的时间。
策策每天都会去逛公园,他总是从 号点进去,从 号点出来。
策策喜欢新鲜的事物,它不希望有两天逛公园的路线完全一样,同时策策还是一个特别热爱学习的好孩子,它不希望每天在逛公园这件事上花费太多的时间。如果 号点到 号点的最短路长为 ,那么策策只会喜欢长度不超过 的路线。
策策同学想知道总共有多少条满足条件的路线,你能帮帮它吗?
为避免输出过大,答案对 取模。
如果有无穷多条合法的路线,请输出 。
给出一张带权有向图,可能有 边,问从 号点到n号点长度不超过最短路长度 的路径有几条,若有无穷多条输出 。
什么时候会有无穷多条?
存在一条长度不超过 的路径,上面可以套一个 环
也就是说,只有有 边的情况才可能有无穷多条
如果不存在 边,就是一个很经典的dp了
设 为 号点到 号点的最短路, 为最短路条数
不存在 边
然后我们发现这个 非常小
尝试把 放到状态里面去
表示从 号点到 号点长度为 的路径条数
如果有 边会发生什么呢?
可能会出现 环
有 环就一定无解吗?
显然不是
回顾一下无穷多解的条件
一条长度 的路径套一个 环
也就是说 环上有一点,经过它的最短路长度
则有无穷多解
我们记录 表示从 号点出发到 号点的最短路
表示从 号点到 号点的最短路,可以把边反向
那么 表示经过 号点的最短路
开一个 数组记录下每一个 是否在访问中
如果访问了一个已经在访问的说明出现了环
同时满足 的话就是
当然,如果 这个状态肯定用不到
直接抛弃掉即可
#include<cstdio>#include<cstring>using namespace std;const int N = 1e5 + 10, K = 51;int n, m, k, p, tot, ans = 0;int first[N], next[N << 1], en[N << 1], w[N << 1];int first1[N * K], next1[N * K << 1], en1[N * K << 1], d[N * K];int q[N << 6], dis[N], dis1[N], u[N << 1], v[N << 1], t[N << 1], f[N * K];bool bz[N];#define gc getchar()inline int read() {int x = 0; char c = gc;while(c < '0' || c > '9') c = gc;while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = gc;return x;}inline void insert(int x, int y, int z) {next[++ tot] = first[x];first[x] = tot;en[tot] = y;w[tot] = z;}inline void insert1(int x, int y) {next1[++ tot] = first1[x];first1[x] = tot;en1[tot] = y;d[y] ++;}inline int get(int x, int y) {return (x - 1) * (k + 1) + y + 1;}int main() {int T = read();while(T --) {n = read(), m = read(), k = read(), p = read();memset(first, tot = 0, sizeof(first));for(int i = 1; i <= m; i ++) {u[i] = read(), v[i] = read(), t[i] = read();insert(u[i], v[i], t[i]);}memset(dis, 60, sizeof(dis));int l = dis[1] = 0, r = q[1] = 1;while(l < r) {int x = q[++ l];bz[x] = false;for(int i = first[x]; i; i = next[i])if(dis[x] + w[i] < dis[en[i]]) {dis[en[i]] = dis[x] + w[i];if(! bz[en[i]]) bz[q[++ r] = en[i]] = true;}}memset(first, tot = 0, sizeof(first));for(int i = 1; i <= m; i ++) insert(v[i], u[i], t[i]);memset(dis1, 60, sizeof(dis1));l = dis1[q[r = 1] = n] = 0;while(l < r) {int x = q[++ l];bz[x] = false;for(int i = first[x]; i; i = next[i])if(dis1[x] + w[i] < dis1[en[i]]) {dis1[en[i]] = dis1[x] + w[i];if(!bz[en[i]]) bz[q[++ r] = en[i]] = true;}}memset(first1, tot = 0, sizeof(first1));memset(d, 0, sizeof(d));for(int i = 1; i <= m; i ++) {int x = get(u[i], 0), y = get(v[i], dis[u[i]] + t[i] - dis[v[i]]);for(int j = dis[u[i]]; j + t[i] + dis1[v[i]] <= dis[n] + k; j ++, x ++, y ++)insert1(x, y);}int num = (k + 1) * n, sum = 0;l = r = ans = 0;memset(f, 0, sizeof(f));for(int i = 1; i <= num; i ++)if(!d[i]) q[++ r] = i;f[1] = 1;while(l < r) {int x = q[++ l];sum ++;for(int i = first1[x]; i; i = next1[i]) {if(! -- d[en1[i]]) q[++ r] = en1[i];f[en1[i]] += f[x];f[en1[i]] = f[en1[i]] > p ? f[en1[i]] - p : f[en1[i]];}}if(sum < num) printf("-1\n");else {for(int i = 0; i <= k; i ++) ans = (ans + f[get(n, i)]) % p;printf("%d\n", ans);}}return 0;}