经典的小球模型。
显然状压是过不了后面的点的。
考虑挖掘哈密尔顿回路的性质,它的性质是:在遍历某棵树时,在这棵树上走过的轨迹必须是一条链,遍历完之后要跳到另一颗树。
所以哈密尔顿回路实际上由若干条链组成。
设链(i)在树(x)中,则相邻的链不能在同一棵树。
这是个经典的问题(小球容斥模型)。
先考虑求出(f_{i,j})表示树(i)被划分成(j)条链的方案数。
这可以通过dp求出。
设(g_{i,j,k})表示(i)子树中选出(j)条链,(i)向子树连出(k)条边。
可以树形背包求。
注意到对于长度(>1)的链(设开头结尾为(a,b)),进入/出它的方案有(2)类:(a)进(b)出,(b)进(a)出,所以答案要(*=2)
先考虑序列问题。
考虑求出每棵树的egf(就是每种"链小球"的egf)。设树被划分为(k)条链。
公式:(=f_{i,k} sumlimits_{j=1}^k (-1)^{(k-j)} inom{k-1}{j-1} frac{x^j}{j!})
原因:(f_{i,k})就是选出(k)条链的方案。
考虑容斥。容斥有多少条链是必须接在一起的。
这些链可以被缩成一个小球。
((-1)^{(k-j)})表示容斥系数。
如果某些链被接在一起且它们相邻,把这两条链(小球)之间连一条边。
(inom{k-1}{j-1})表示在当前链序列中选择(j)个空位,插入边的方案。
对于每个(k)求和就是整棵树的生成函数。
但是还要考虑处理环的问题。
考虑钦定一颗树,使第一条链一定来自这棵树。
由于第一条链必须在第一位,生成函数为(sumlimits_{k=1}^{k_i} frac{f_{i,k}}{k} sumlimits_{j=1}^k (-1)^{(k-j)} inom{k-1}{j-1} frac{x^{j-1}}{(j-1)!})
前面要除以(k)是因为当前树可能有(k)条链作为起始位置,导致算重(k)次。
但是发现第一棵树不能有相邻的两条链,所以第一颗树的生成函数要减去(sumlimits_{k=1}^{k_i} frac{f_{i,k}}{k} sumlimits_{j=1}^k (-1)^{(k-j)} inom{k-1}{j-1} frac{x^{j-2}}{(j-2)!})
最后把所有树的生成函数卷积(暴力即可)就是答案。
时间复杂度还是(O(n^2))。
证明:设我们要卷积的多项式大小为(a_1,a_2...a_k)
构造一颗树:把(a_i)裂成(i)个点连成链,然后把(a_{i-1})的链尾连向(a_i)的链头。
如果倒着进行卷积,时间复杂度一定低于于在这条链上进行树形背包的时间复杂度。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define mo 998244353
#define N 5010
int t[N],jc[N],ij[N],iv[N],h[N],v[N*2],nxt[N*2],ec,m,sz[N],ans[N],va,tp[N][3],sc[N],f[N][N][3],c[N],i2;
int cc(int y,int x){
if(y<0||x<0||y<x)
return 0;
return jc[y]*ij[x]%mo*ij[y-x]%mo;
}
void add(int x,int y){
v[++ec]=y;
nxt[ec]=h[x];
h[x]=ec;
}
void ml(int *a,int *b,int *c,int d,int e){
for(int i=0;i<=d+e;i++)
t[i]=0;
for(int i=0;i<=d;i++)
for(int j=0;j<=e;j++)
t[i+j]=(t[i+j]+a[i]*b[j]%mo)%mo;
for(int i=0;i<=d+e;i++)
c[i]=t[i];
}
int qp(int x,int y){
int r=1;
for(;y;y>>=1,x=x*x%mo)
if(y&1)
r=r*x%mo;
return r;
}
void dfs(int x,int fa){
sz[x]=f[x][1][0]=1;
for(int i=h[x];i;i=nxt[i])
if(v[i]!=fa){
int y=v[i];
dfs(y,x);
for(int j=1;j<=sz[y];j++){
int tv=(f[y][j][0]+f[y][j][1]+f[y][j][2])%mo;
for(int k=1;k<=sz[x];k++){
tp[j+k][0]=(tp[j+k][0]+tv*f[x][k][0]%mo)%mo;
tp[j+k][1]=(tp[j+k][1]+tv*f[x][k][1]%mo)%mo;
tp[j+k][2]=(tp[j+k][2]+tv*f[x][k][2]%mo)%mo;
tp[j+k-1][1]=(tp[j+k-1][1]+f[x][k][0]*(f[y][j][1]+2*f[y][j][0]%mo)%mo)%mo;
tp[j+k-1][2]=(tp[j+k-1][2]+f[x][k][1]*(i2*f[y][j][1]%mo+f[y][j][0])%mo)%mo;
}
}
sz[x]+=sz[y];
for(int j=0;j<=sz[x];j++){
f[x][j][0]=tp[j][0];
f[x][j][1]=tp[j][1];
f[x][j][2]=tp[j][2];
tp[j][0]=tp[j][1]=tp[j][2]=0;
}
}
}
signed main(){
i2=(mo+1)/2;
jc[0]=ij[0]=1;
for(int i=1;i<N;i++){
jc[i]=jc[i-1]*i%mo;
ij[i]=qp(jc[i],mo-2);
iv[i]=qp(i,mo-2);
}
int sn=0;
ans[0]=1;
scanf("%lld",&m);
for(int i=m;i;i--){
int n;
scanf("%lld",&n);
for(int j=1;j<n;j++){
int x,y;
scanf("%lld%lld",&x,&y);
add(x,y);
add(y,x);
}
dfs(1,0);
for(int j=1;j<=n;j++)
c[j]=(f[1][j][0]+f[1][j][1]+f[1][j][2])%mo*jc[j]%mo;
for(int j=1;j<=n;j++){
int va=0;
for(int k=j;k<=n;k++){
int bp=1,ii=1;
if(i==1)
ii=iv[k];
if((k-j)&1)
bp=mo-1;
va=(va+bp*c[k]%mo*cc(k-1,j-1)%mo*ii%mo+mo)%mo;
}
if(i==1){
sc[j-1]=va;
if(j>=2)
sc[j-2]=(sc[j-2]-va+mo)%mo;
}
else
sc[j]=va;
}
for(int j=0;j<=n;j++)
sc[j]=sc[j]*ij[j]%mo;
ml(ans,sc,ans,sn,n);
sn+=n;
for(int j=1;j<=ec;j++)
nxt[j]=v[j]=0;
for(int j=1;j<=n;j++){
h[j]=sc[j]=c[j]=0;
for(int k=1;k<=n;k++)
f[j][k][0]=f[j][k][1]=f[j][k][2]=0;
}
ec=0;
}
for(int i=1;i<=sn;i++)
va=(va+jc[i]*ans[i])%mo;
printf("%lld",va);
}