一、题目
二、解法
有一个神奇的题意转化:我们把每一棵树划分成若干条链,因为不同的树任意两点之间都有边,所以我们把这些链任意连接就形成哈密顿回路,要求是相邻的链必须来自不同的树。
首先我们考察把树划分成 \(i\) 条链的方案数 \(f_i\),可以直接树背包,在确定一条链并且这条链非单点的时候乘上 \(2\) 的系数。
然后考虑怎么把链任意连接,可以写出以链数为标记的 \(\tt EGF\),那么答案的 \(\tt EGF\) 就是所有树 \(\tt EGF\) 的卷积。由于第一棵树是不一样的,我们先考虑写出其他树的 \(\tt EGF\):
\[\sum_{i=1}^n f_i\cdot i!\sum_{j=1}^i(-1)^{i-j}\cdot {i-1\choose i-j}\cdot \frac{x^j}{j!}
\]
首先是把这些链任意排列的方案数,即 \(f_i\cdot i!\);由于还有相邻链必须来自不同树的限制,所以要容斥。我们枚举最后形成了 \(j\) 条大链(相邻的同色链缩合在一起),那么就有 \(i-j\) 个空隙需要拼合,方案数是 \({i-1\choose i-j}\),容斥系数是 \((-1)^{i-j}\)
考虑写出第一棵树的 \(\tt EGF\),额外的限制是:起点必须是 \(1\) 号点。那么我们把 \(1\) 号点所在的链放在首位就行了,并且让它不参与和其他树 \(\tt EGF\) 的合并:
\[\sum_{i=1}^n f_i\cdot (i-1)!\sum_{j=1}^i(-1)^{i-j}\cdot {i-1\choose i-j}\cdot \frac{x^{j-1}}{(j-1)!}
\]
还有一个限制是:最后一条链不能是第一棵树的链,那么我们减去这种情况即可,可以强制一条链放在最后,让它不参与和其他树 \(\tt EGF\) 的合并,所以要减去这样的 \(\tt EGF\):
\[\sum_{i=1}^nf_i\cdot (i-1)!\sum_{j=2}^i (-1)^{i-j}\cdot{i-1\choose i-j}\cdot\frac{x^{j-2}}{(j-2)!}
\]
暴力实现卷积,时间复杂度 \(O(n^2)\)
#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
const int M = 5005;
const int MOD = 998244353;
#define int long long
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int T,n,m,siz[M],dp[M][M][2],t[M][3];
int ans,fac[M],inv[M],f[M],g[M],z[M];
vector<int> e[M];
void add(int &x,int y) {x=(x+y)%MOD;}
void init(int n)
{
fac[0]=inv[0]=inv[1]=1;
for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%MOD;
for(int i=2;i<=n;i++) inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=2;i<=n;i++) inv[i]=inv[i-1]*inv[i]%MOD;
}
int C(int n,int m)
{
if(n<m || m<0) return 0;
return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}
void dfs(int u,int fa)
{
dp[u][0][0]=siz[u]=1;
for(int v:e[u]) if(v^fa) dfs(v,u);
for(int v:e[u]) if(v^fa)
{
for(int i=0;i<siz[u];i++)
for(int j=1;j<=siz[v];j++)
{
add(t[i+j][0],dp[u][i][0]*dp[v][j][0]);
add(t[i+j][1],dp[u][i][1]*dp[v][j][0]);
add(t[i+j][1],dp[u][i][0]*dp[v][j][1]);
add(t[i+j-1][2],dp[u][i][1]*dp[v][j][1]*2);
add(t[i+j][2],f[i]*dp[v][j][0]);
}
siz[u]+=siz[v];
for(int i=0;i<siz[u];i++)
{
dp[u][i][0]=t[i][0];
dp[u][i][1]=t[i][1];
f[i]=t[i][2];
t[i][0]=t[i][1]=t[i][2]=0;
}
for(int j=0;j<=siz[v];j++)
dp[v][j][0]=dp[v][j][1]=0;
}
for(int i=siz[u];i>=1;i--)
{
dp[u][i][0]=(dp[u][i-1][0]+f[i]+dp[u][i][1]*2)%MOD;
dp[u][i][1]=(dp[u][i-1][0]+dp[u][i][1])%MOD;
f[i]=0;
}
e[u].clear();
}
void work()
{
n=read();static int start=1;
for(int i=1;i<n;i++)
{
int u=read(),v=read();
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1,0);
for(int i=1;i<=n;i++) f[i]=0;
if(start)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)
{
int t=dp[1][i][0]*fac[i-1]%MOD*C(i-1,i-j)
%MOD*inv[j-1]%MOD;
if((i-j)&1) add(f[j-1],MOD-t);
else add(f[j-1],t);
if(j>1)
{
t=t*(j-1)%MOD;
if((i-j)&1) add(f[j-2],t);
else add(f[j-2],MOD-t);
}
}
start=0;
}
else
{
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)
{
int t=dp[1][i][0]*fac[i]%MOD*C(i-1,i-j)
%MOD*inv[j]%MOD;
if((i-j)&1) add(f[j],MOD-t);
else add(f[j],t);
}
}
for(int i=0;i<=m;i++)
for(int j=0;j<=n;j++)
add(z[i+j],f[j]*g[i]);
m+=n;
for(int i=0;i<=m;i++)
g[i]=z[i],z[i]=0;
for(int i=0;i<=n;i++)
dp[1][i][0]=dp[1][i][1]=f[i]=0;
}
signed main()
{
T=read();init(5000);g[0]=1;
while(T--) work();
for(int i=0;i<=m;i++) add(ans,g[i]*fac[i]);
printf("%lld\n",ans);
}