题解
如果一开始的图就不是仙人掌,答案显然为0,可以Tarjan判断。
环显然不能产生贡献,所以可以把环边都断开。
现在模型转化为,给定一棵树,用路径去覆盖树上的每一条边,且路径不能相交,求方案数。
设
fi
表示做完了
i
的子树,且没有路径可以向上扩展。
设
设
记
fx=Πgson×hnum
gx=fx+Πgson×hnum−1×num
简单解释一下:
hi 转移的时候考虑当前第 i 个儿子的选择,如果这个儿子不匹配,那就有
fx 的转移:每个儿子都必须要可以往上扩,且各个儿子之间相对独立所以是 Πgson ,然后一共有 hnum 种儿子的匹配方案,所以乘起来就是所有可能的方案。
gx 的转移:首先 x 自己可以往上扩展,方案就是
时间复杂度: O(n)
SRC
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std ;
#define N 500000 + 10
#define M 1000000 + 10
typedef long long ll ;
const int MO = 998244353 ;
bool is_cactus ;
bool vis[N] , flag[2*M] ;
int Node[2*M] , From[2*M] , Next[2*M] , Head[N] , tot ;
int S[N] , DFN[N] , LOW[N] , Col[N] , Tim , top ;
int f[N] , g[N] , h[N] ;
int Case , n , m ;
ll ans ;
int Read() {
int ret = 0 ;
char ch = getchar() ;
while ( ch < '0' || ch > '9' ) ch = getchar() ;
while ( ch >= '0' && ch <= '9' ) {
ret = ret * 10 + ch - '0' ;
ch = getchar() ;
}
return ret ;
}
void Pre() {
h[0] = 1 ;
for (int i = 1 ; i <= 500000 ; i ++ ) {
h[i] = h[i-1] + ( i > 1 ? 1ll * h[i-2] * (i - 1) % MO : 0 ) ;
if ( h[i] >= MO ) h[i] %= MO ;
}
}
void link( int u , int v ) {
Node[++tot] = v ;
From[tot] = u ;
Next[tot] = Head[u] ;
Head[u] = tot ;
flag[tot] = 1 ;
}
void Tarjan( int x , int F ) {
DFN[x] = LOW[x] = ++ Tim ;
S[++top] = x ;
bool flag = 0 ;
for (int p = Head[x] ; p ; p = Next[p] ) {
if ( Node[p] == F ) continue ;
if ( !DFN[Node[p]] ) {
Tarjan( Node[p] , x ) ;
LOW[x] = min( LOW[x] , LOW[Node[p]] ) ;
if ( LOW[Node[p]] < DFN[x] ) {
if ( flag ) { is_cactus = 0 ; return ; }
flag = 1 ;
}
} else {
LOW[x] = min( LOW[x] , DFN[Node[p]] ) ;
if ( DFN[Node[p]] < DFN[x] ) {
if ( flag ) { is_cactus = 0 ; return ; }
flag = 1 ;
}
}
}
if ( DFN[x] == LOW[x] ) {
while ( 1 ) {
Col[S[top]] = x ;
top -- ;
if ( S[top+1] == x ) break ;
}
}
}
void DFS( int x , int F ) {
vis[x] = 1 ;
f[x] = 1 , g[x] = 0 ;
int num = 0 ;
for (int p = Head[x] ; p ; p = Next[p] ) {
if ( !flag[p] || Node[p] == F ) continue ;
DFS( Node[p] , x ) ;
f[x] = 1ll * f[x] * g[Node[p]] % MO ;
num ++ ;
}
g[x] = (1ll * f[x] * h[num] % MO + 1ll * f[x] * h[num-1] % MO * num % MO) % MO ;
f[x] = 1ll * f[x] * h[num] % MO ;
}
int main() {
freopen( "cactus.in" , "r" , stdin ) ;
freopen( "cactus.out" , "w" , stdout ) ;
Pre() ;
Case = Read() ;
while ( Case -- ) {
is_cactus = 1 , ans = 1 ;
tot = Tim = top = 0 ;
n = Read() , m = Read() ;
for (int i = 1 ; i <= n ; i ++ ) {
vis[i] = 0 ;
Head[i] = Col[i] = DFN[i] = LOW[i] = 0 ;
}
for (int i = 1 ; i <= m ; i ++ ) {
int u = Read() , v = Read() ;
link( u , v ) ;
link( v , u ) ;
}
Tarjan( 1 , 0 ) ;
if ( !is_cactus ) { printf( "0\n" ) ; continue ; }
for (int i = 1 ; i <= tot ; i ++ ) if ( Col[From[i]] == Col[Node[i]] ) flag[i] = 0 ;
for (int i = 1 ; i <= n ; i ++ ) {
if ( vis[i] ) continue ;
DFS( i , 0 ) ;
ans = ans * f[i] ;
if ( ans >= MO ) ans %= MO ;
}
printf( "%lld\n" , ans ) ;
}
return 0 ;
}
以上.