《P3953 [NOIP2017 提高组] 逛公园》
这题之前没做出来,现在看懂了。
因为k很小,所以考虑求出所有恰好满足 dis <= len <= dis + k的路径数。
这里就是用了枚举的思想,去枚举每种情况。
然后dp[i][j]表示从1走到i点用了比最短路多j的路径的方案数。
然后去转移。
有两种思路,从n开始倒着搜dp,比较容易理解。
我这里是正着来dp了。
从u -> v 的w路径,比到v的最短路多了dis[v] + len - dis[u] - val。还剩下这么多路径,如果 < 0就说明不可以走到。
不小于就可以继续dp转移。
注意这里的一个问题就是有两种情况的零环。
1:有零环,且会导致无穷解的出现,那么就在dfs的途中对每种情况都做一个记录,这样第二次走到的时候就说明有零环了。
2:有零环,但是从1 -> n的路径中零环不会走入,或者就算进入零环,环两边的最小路径也 > dis + k.
对于第二种情况,我们做两次正着 和 反着的dij是否能走到,是否环两边最小路径 > dis + k。
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> pii; const int N = 2e5 + 5; const int M = 3e5 + 5; const LL Mod = 1e9 + 7; #define pi acos(-1) #define INF 1e9 #define dbg(ax) cout << "now this num is " << ax << endl; namespace FASTIO{ inline LL read(){ LL x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } } using namespace FASTIO; int n,m,k,p,dis1[N],dis2[N],dp[N][55]; bool tag[N],f[N][55]; struct Node{int to,dis;}; vector<Node> G[N],RG[N]; void dij(int x,int dis[],vector<Node> E[]){ for(int i = 1;i <= n;++i) dis[i] = INF; dis[x] = 0; priority_queue<pii,vector<pii>,greater<pii> > Q; Q.push(pii{dis[x],x}); while(!Q.empty()){ int u = Q.top().second; int d = Q.top().first; Q.pop(); if(d > dis[u]) continue; for(auto v : E[u]){ if(dis[v.to] > dis[u] + v.dis){ dis[v.to] = dis[u] + v.dis; Q.push(pii{dis[v.to],v.to}); } } } } LL dfs(int u,int len){ //printf("u is %d len is %d\n",u,len); if(f[u][len] != 0) return -1; if(dp[u][len] != -1) return dp[u][len]; f[u][len] = 1; LL sum = 0; if(u == n && len == 0) sum++; for(auto v : G[u]){ if(tag[v.to] == 0) continue; int x = len + dis1[v.to] - dis1[u] - v.dis; if(x < 0) continue; int tmp = dfs(v.to,x); if(tmp == -1) return -1; sum = (sum + tmp) % p; } f[u][len] = 0; return dp[u][len] = sum; } int main() { int ca;ca = read(); while(ca--){ n = read(),m = read(),k = read(),p = read(); for(int i = 1;i <= n;++i) G[i].clear(),RG[i].clear(),tag[i] = 0; memset(dp,-1,sizeof(dp)); memset(f,0,sizeof(f)); while(m--){ int a,b,c;a = read(),b = read(),c = read(); G[a].push_back(Node{b,c}); RG[b].push_back(Node{a,c}); } dij(1,dis1,G); dij(n,dis2,RG); for(int i = 1;i <= n;++i){ if(dis1[i] == INF || dis2[i] == INF) continue; if(dis1[i] + dis2[i] > k + dis1[n]) continue; tag[i] = 1; } LL ans = 0; bool flag = false; for(int kk = 0;kk <= k;++kk){ LL sum = dfs(1,kk); if(sum == -1) flag = true; else ans = (ans + sum) % p; // printf("sum is %lld flag is %d\n",sum,flag); } if(flag) printf("-1\n"); else printf("%lld\n",ans); } system("pause"); return 0; }