【洛谷】P3953 逛公園
阿新 • • 發佈:2018-12-18
類似於最短路計數讓我們想到 Dp
首先 K <= 50,看到這個想到 Dp 是二維的,定義 Dp(i,k)為 i 到終點路徑長度比最短路多 k 的路徑總數
然後就不會轉移了
然後怎麼轉移呢,Dp(u,k)與 Dp(i,k)的聯絡,:
最好,建立一個虛擬節點,因為到終點可以往回走
Minpath(i)表示 i 到終點的距離
那麼走 w 這條邊比最短路多的就是 Minpath(v)+ W(i)- Minpath(u)
轉移到 k 就可以用 k - (Minpath(v)+ W(i)- Minpath(u))轉移
判 0 環用 ins 判狀態是否被重複訪問(有 0 才會重複訪問),如果這個狀態已經被訪問過了,那麼直接 GG
最後答案為:
注意初值,dp [ n + 1 ] [ 0 ] = 1 , dp [ n + 1 ] [ 1 ~ k ] = 0 , dp [ 1 ~ n ] [ 0 ~ k ] = - 1
只要有一次 Dfs 有無窮多種,直接輸出 - 1,否則累加
// luogu-judger-enable-o2 # include <bits/stdc++.h> const int N = 500000 + 5 , K = 50 + 5 ; int head [ N ] , nxt [ N << 1 ] , to [ N << 1 ] , w [ N << 1 ] , cn ; int headv [ N ] , nxtv [ N << 1 ] , tov [ N << 1 ] , wv [ N << 1 ] , cnv ; int dp [ N ] [ K ] , dis [ N ] , n , inf = 1e9 + 7 , mod , k , T , m , x , y , z , ans ; bool vis [ N ] , ins [ N ] [ K ] ; void create ( int u , int v , int d ) { cn ++ ; w [ cn ] = d ; to [ cn ] = v ; nxt [ cn ] = head [ u ] ; head [ u ] = cn ; } void create_v ( int u , int v , int d ) { cnv ++ ; wv [ cnv ] = d ; tov [ cnv ] = v ; nxtv [ cnv ] = headv [ u ] ; headv [ u ] = cnv ; } std :: queue < int > q ; void spfa ( ) { memset ( dis , 0x3f , sizeof ( dis ) ) ; memset ( vis , false , sizeof ( vis ) ) ; memset ( ins , false , sizeof ( ins ) ) ; q . push ( n + 1 ) ; dis [ n + 1 ] = 0 ; while ( ! q . empty ( ) ) { int u = q . front ( ) ; q . pop ( ) ; vis [ u ] = false ; for ( int i = headv [ u ] ; i ; i = nxtv [ i ] ) { int v = tov [ i ] ; if ( dis [ v ] > dis [ u ] + wv [ i ] ) { dis [ v ] = dis [ u ] + wv [ i ] ; if ( ! vis [ v ] ) { q . push ( v ) ; vis [ v ] = true ; } } } } } int dfs ( int u , int k ) { if ( ins [ u ] [ k ] ) return inf ; if ( dp [ u ] [ k ] != - 1 ) return dp [ u ] [ k ] ; dp [ u ] [ k ] = 0 ; ins [ u ] [ k ] = true ; for ( int i = head [ u ] ; i ; i = nxt [ i ] ) { int v = to [ i ] ; int delta = dis [ u ] - w [ i ] - dis [ v ] + k ; if ( delta < 0 ) continue ; if ( dfs ( v , delta ) == inf ) return dp [ u ] [ k ] = inf ; dp [ u ] [ k ] = ( dp [ u ] [ k ] + dp [ v ] [ delta ] ) % mod ; } ins [ u ] [ k ] = false ; return dp [ u ] [ k ] ; } void init ( ) { cn = cnv = ans = 0 ; memset ( head , 0 , sizeof ( head ) ) ; memset ( headv , 0 , sizeof ( headv ) ) ; memset ( dp , - 1 , sizeof ( dp ) ) ; dp [ n + 1 ] [ 0 ] = 1 ; for ( int i = 1 ; i <= k ; i ++ ) dp [ n + 1 ] [ i ] = 0 ; } int main ( ) { scanf ( "%d" , & T ) ; while ( T -- ) { scanf ( "%d%d%d%d" , & n , & m , & k , & mod ) ; init ( ) ; for ( int i = 1 ; i <= m ; i ++ ) scanf ( "%d%d%d" , & x , & y , & z ) , create ( x , y , z ) , create_v ( y , x , z ) ; create ( n , n + 1 , 0 ) ; create_v ( n + 1 , n , 0 ) ; spfa ( ) ; for ( int i = 0 ; i <= k ; i ++ ) { if ( dfs ( 1 , i ) == inf ) { ans = - 1 ; break ; } ans = ( ans + dp [ 1 ] [ i ] ) % mod ; } printf ( "%d\n" , ans ) ; } return 0 ; }