1. 程式人生 > >【洛谷】P3953 逛公園

【洛谷】P3953 逛公園

類似於最短路計數讓我們想到 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 

最後答案為:

\sum _{i=0}^{k} dp[1][i]

注意初值,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 ; 
}