Luogu P3953 逛公園 (最短路+dp)

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\n");
        else printf("%d\n",res);
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章