题目大意
给定一棵
n
个点的树,每个点有标号,每次操作可以选择一条边删除,然后重新连一条边。
现在要求,在进行不超过
Data Constraint
n≤50
题解
注意到实际上要求的就是最多有不超过
k
条不在原图中的边构成的生成树个数。
如果没有操作,显然就是基础的矩阵树定理。
矩阵树定理
定义一个图
那么它的基尔霍夫矩阵
C=D−A
。
那么这个图所能构造的生成树个数就是
C
的任意一个
考虑如何在这题中运用矩阵树定理。
可以把原图的一棵树看做一个图,如果一条边
(i,j)
存在,那么
Ai,j=1
,否则就给
Ai,j
赋一个多项式
x
,度数矩阵类似。
然后求余子式,就会得到一个多项式。然后可以发现
现在问题是如何求出这个多项式。可以给
x
赋
可以用拉格朗日插值实现,我是用高斯消元解的。
时间复杂度: O(n4)
SRC
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<map>
using namespace std ;
#define N 50 + 10
typedef long long ll ;
const int MO = 998244353 ;
struct Note {
ll x , y ;
Note ( ll X = 0 , ll Y = 0 ) { x = X , y = Y ; }
} A[N][N] ;
bool flag[N] , Map[N][N] ;
int g[N][N] , Xi[N] , Yi[N] ;
int n , m , Sig ;
ll ans ;
Note operator - ( Note a , Note b ) { return Note( (a.x - b.x + MO) % MO , (a.y - b.y + MO) % MO ) ; }
int Power( int x , int k ) {
int s = 1 ;
while ( k ) {
if ( k & 1 ) s = (ll)s * x % MO ;
x = (ll)x * x % MO ;
k /= 2 ;
}
return s ;
}
void Gauss( int n , int m ) {
memset( flag , 0 , sizeof(flag) ) ;
for (int i = 1 ; i <= n ; i ++ ) {
int wz = 0 ;
for (int j = 1 ; j <= n ; j ++ ) {
if ( flag[j] ) continue ;
if ( g[j][i] ) {
wz = j ;
break ;
}
}
if ( !wz ) continue ;
if ( wz != i ) {
swap( g[wz] , g[i] ) ;
Sig *= -1 ;
}
flag[i] = 1 ;
int Ni = Power( g[i][i] , MO - 2 ) ;
for (int j = 1 ; j <= n ; j ++ ) {
if ( i == j || !g[j][i] ) continue ;
int d = (ll)g[j][i] * Ni % MO ;
for (int k = 1 ; k <= m ; k ++ ) {
g[j][k] = ((g[j][k] - (ll)g[i][k] * d % MO) % MO + MO) % MO ;
}
}
}
}
int Calc( int x ) {
for (int i = 1 ; i < n ; i ++ ) {
for (int j = 1 ; j < n ; j ++ ) {
g[i][j] = (A[i][j].x * x % MO + A[i][j].y) % MO ;
}
}
Sig = 1 ;
Gauss( n - 1 , n - 1 ) ;
int ret = 1 ;
for (int i = 1 ; i < n ; i ++ ) ret = (ll)ret * g[i][i] % MO ;
return ret * Sig ;
}
int main() {
freopen( "b.in" , "r" , stdin ) ;
freopen( "b.out" , "w" , stdout ) ;
scanf( "%d%d" , &n , &m ) ;
m = min( m , n - 1 ) ;
for (int i = 2 ; i <= n ; i ++ ) {
int f ;
scanf( "%d" , &f ) ;
f ++ ;
Map[f][i] = Map[i][f] = 1 ;
}
for (int i = 1 ; i <= n ; i ++ ) {
for (int j = 1 ; j <= n ; j ++ ) {
if ( i == j ) continue ;
if ( Map[i][j] ) A[i][j] = Note( 0 , MO - 1 ) ;
else A[i][j] = Note( MO - 1 , 0 ) ;
A[i][i] = A[i][i] - A[i][j] ;
}
}
for (int i = 0 ; i < n ; i ++ ) {
Xi[i] = i ;
Yi[i] = Calc(i) ;
}
for (int i = 1 ; i <= n ; i ++ ) {
int s = 1 ;
for (int j = 1 ; j <= n ; j ++ ) {
g[i][j] = s ;
s = (ll)s * Xi[i-1] % MO ;
}
g[i][n+1] = Yi[i-1] ;
}
Gauss( n , n + 1 ) ;
for (int i = 1 ; i <= m + 1 ; i ++ )
ans = (ans + (ll)g[i][n+1] * Power( g[i][i] , MO - 2 ) % MO) % MO ;
printf( "%lld\n" , ans ) ;
return 0 ;
}
以上.