Luogu P3953 逛公园 (最短路+dp)
题意:
一个有向图,找从1到n的长度在 d 到 d + k 之间的路径个数模 p 的值。(d是1到n的最短路,k,p由输入给出)。
思路:
首先想到dp[i][j]表示1到 i 距离为j的路径个数,那么dp[n][d] + ... + dp[n][d + k] 就是答案。但是数组开不了这么大,且复杂度不对。但dp的大方向应该不会错。
我们注意到k很小,所以可以把dp二维 j 表示改为从1 到 i 距离小于d + j 的路径个数。这样就可以了。那么转移方程就是:
[dp[u][j] = sum dp[v][(d[u] + j) - w - d[v]]
]
其中d[i] 表示从i 到 n 的最短路,这个我们可以在原图的反向图上跑Dijkstra求出。那么 d[u]+ j 就是 u 到 n 点的距离,减 w 就是此时 v 到 n的距离,再减d[v] 就是 v 到 n的距离比v到 n的最短路d[v] 大的值,即dp数组二维的含义。
我们可以从1点开始dfs,去计算结果,具体过程见代码。
这里要注意这种情况:存在权值和为0的环时,路径可以无限在环上绕,有无限中路径,要输出-1。判断这种情况只需要开一个flag[][]数组,在一个点处理dp[u][j] 时,看会不会再处理一次dp[u][j], 如果再次处理说明转移过程中有个环且环的权值和为0。直接return -1。具体见代码。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
#include<queue>
#include<vector>
#include<string>
#include<bitset>
#include<fstream>
using namespace std;
#define rep(i, a, n) for(int i = a; i <= n; ++ i);
#define per(i, a, n) for(int i = n; i >= a; -- i);
typedef long long ll;
typedef pair<int,int> PII;
const int N = 2e5 + 105;
const int mod = 1e9 + 7;
const double Pi = acos(- 1.0);
const int INF = 0x3f3f3f3f;
const int G = 3, Gi = 332748118;
ll qpow(ll a, ll b) { ll res = 1; while(b){ if(b & 1) res = (res * a) % mod; a = (a * a) % mod; b >>= 1;} return res; }
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
// bool cmp(int a, int b){return a > b;}
//
int n, m; int k, p;
int head[N << 1], cnt = 0;
int rhead[N << 1], rcnt = 0;
int to[N << 1], nxt[N << 1]; int c[N << 1];
int rto[N << 1], rnxt[N << 1]; int rc[N << 1];
queue<int> q;
int dp[N][60];
bool flag[N][60];
void add(int u, int v, int w){
to[cnt] = v, c[cnt] = w, nxt[cnt] = head[u], head[u] = cnt ++;
rto[rcnt] = u, rc[rcnt] = w, rnxt[rcnt] = rhead[v], rhead[v] = rcnt ++;
}
//Dijkstra
int d[N << 1];
void Dijkstra(int st)
{
priority_queue<PII, vector<PII>, greater<PII> > q;
for(int i = 1; i <= n + 1; ++ i) d[i] = INF;
d[st] = 0;
q.push(PII(0, st));
while(!q.empty()){
int u = q.top().second; int w1 = q.top().first; q.pop();
if(w1 != d[u]) continue;
for(int i = rhead[u]; i != -1; i = rnxt[i]){
int v = rto[i]; int w2 = rc[i];
if(d[u] + w2 < d[v]){
d[v] = d[u] + w2;
q.push(PII(d[v], v));
}
}
}
}
int dfs(int u, int t){
if(t < 0 || t > k) return 0;
if(flag[u][t]) return -1;
if(dp[u][t]) return dp[u][t];
flag[u][t] = 1;
int sum = 0;
for(int i = head[u]; i != -1; i = nxt[i]){
int v = to[i]; int w = c[i];
int tp = dfs(v, t + d[u] - w - d[v]);
if(tp == -1) return -1;
sum = (sum + tp) % p;
}
if(u == n && t == 0) sum ++;
dp[u][t] = sum;
flag[u][t] = 0;
return sum;
}
int main()
{
int T;
scanf("%d",&T);
while(T --){
scanf("%d%d%d%d",&n,&m,&k,&p);
cnt = rcnt = 0;
for(int i = 0; i <= n; ++ i) head[i] = rhead[i] = -1;
memset(flag, false, sizeof(flag));
memset(dp, 0, sizeof(dp));
for(int i = 1; i <= m; ++ i){
int x, y; int z; scanf("%d%d%d",&x,&y,&z);
add(x, y, z);
}
Dijkstra(n);
int res = 0; bool fl = 0;
for(int i = 0; i <= k; ++ i){
int tp = dfs(1, i);
if(tp == -1){
fl = 1;
break;
}
res = (res + tp) % p;
}
if(fl) printf("-1
");
else printf("%d
",res);
}
return 0;
}