正题
题目链接:https://www.luogu.com.cn/problem/P5333
题目大意
给出nnn棵树,第iii棵树有kik_iki个点,每棵树上的每个点和其它树上的所有点都有连边。
求这棵树有多少条哈密顿回路。
答案对998244353998244353998244353取模。
∑i=1nki≤5000\sum_{i=1}^nk_i\leq 5000∑i=1nki≤5000
解题思路
我们把每棵树分成若干条路径,那么这个哈密顿回路肯定是这些路径拼起来,并且相邻的路径不能来自于同一棵树。
那么对于每棵树我们先算出一个gig_igi表示将这棵树分成iii条路径的方案数。
至于相邻的路径不能来自于同一棵树的限制我们考虑容斥,我们硬将一棵树上iii条路径相邻的放那么容斥系数就是(−1)i(-1)^i(−1)i。
然后背包出总共iii条路径的方案,像可重排一样在前面一棵树上jjj条路径的就乘上1j!\frac1{j!}j!1,最后乘上i!i!i!就好了。
然后为了防止算重我们固定从111出发,那么处理第一棵树的时候我们就默认有一个放最前面,然后再减去头尾都来自第111棵树的方案就好了。
时间复杂度:O((∑ki)2)O((\sum k_i)^2)O((∑ki)2)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=5100,P=998244353;
struct node{ll to,next;
}a[N<<1];
ll n,m,sum,tot,ls[N],g[N],h[N];
ll siz[N],f[N][N][3],tmp[N][3];
ll fac[N],inv[N];
void addl(ll x,ll y){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;return;
}
void dfs(ll x,ll fa){siz[x]=1;f[x][0][2]=1;for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(y==fa)continue;dfs(y,x);for(ll j=0;j<=siz[x];j++)for(ll k=0;k<=siz[y];k++){(tmp[j+k][2]+=f[x][j][2]*f[y][k][0]%P)%=P;(tmp[j+k][1]+=f[x][j][2]*f[y][k][1]%P)%=P;(tmp[j+k][1]+=f[x][j][1]*f[y][k][0]%P)%=P;(tmp[j+k][0]+=f[x][j][0]*f[y][k][0]%P)%=P;(tmp[j+k+1][0]+=f[x][j][1]*f[y][k][1]*2ll%P)%=P;}siz[x]+=siz[y];for(ll j=0;j<=siz[x];j++)for(ll k=0;k<3;k++)f[x][j][k]=tmp[j][k],tmp[j][k]=0;}for(ll i=0;i<=siz[x];i++){(f[x][i+1][0]+=f[x][i][1]*2ll)%=P;(f[x][i+1][0]+=f[x][i][2])%=P;(f[x][i][1]+=f[x][i][2])%=P;}return;
}
ll C(ll n,ll m)
{return fac[n]*inv[m]%P*inv[n-m]%P;}
signed main()
{fac[0]=inv[0]=inv[1]=h[0]=1;for(ll i=2;i<N;i++)inv[i]=P-inv[P%i]*(P/i)%P;for(ll i=1;i<N;i++)fac[i]=fac[i-1]*i%P,inv[i]=inv[i-1]*inv[i]%P;scanf("%lld",&m);for(ll p=1;p<=m;p++){scanf("%lld",&n);for(ll i=1;i<=n;i++)ls[i]=0;tot=0;for(ll i=1,x,y;i<n;i++){scanf("%lld%lld",&x,&y);addl(x,y);addl(y,x);}dfs(1,0);sum+=n;for(ll i=0;i<=n;i++)g[i]=0;if(p!=1){for(ll i=1;i<=n;i++)for(ll j=i;j<=n;j++)(g[i]+=f[1][j][0]*fac[j]%P*C(j-1,i-1)%P*(((j-i)&1)?(P-1):1)%P)%=P;}else{for(ll i=1;i<=n;i++)for(ll j=i;j<=n;j++)(g[i-1]+=f[1][j][0]*fac[j-1]%P*C(j-1,i-1)%P*(((j-i)&1)?(P-1):1)%P)%=P;for(ll i=2;i<=n;i++)for(ll j=i;j<=n;j++)(g[i-2]+=f[1][j][0]*fac[j-1]%P*C(j-1,i-1)%P*(((j-i)&1)?1:(P-1))%P)%=P;}for(ll i=1;i<=n;i++)g[i]=g[i]*inv[i]%P;for(ll j=sum;j>=0;j--){ll tmp=0;for(ll i=0;i<=min(n,j);i++)(tmp+=h[j-i]*g[i]%P)%=P;h[j]=tmp;}for(ll i=1;i<=n;i++)for(ll j=0;j<=siz[i];j++)for(ll k=0;k<3;k++)f[i][j][k]=0;}ll ans=0;for(ll i=0;i<=sum;i++)(ans+=h[i]*fac[i]%P)%=P;printf("%lld\n",ans);return 0;
}