类似于最短路计数让我们想到 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
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 ;
}