题意:
给出一副连通图,有 n n n个节点,他们有 n − 1 n-1 n−1条边,将这 n − 1 n-1 n−1条边两两分组,使得每一组的两条边需要有一个公共点,保证 n n n是奇数,求有多少种分组方法
Solution:
一副 n n n个节点的连通图,并且有 n − 1 n-1 n−1条边,这显然是一颗树
我们不妨分点考虑,如果当前点有关的边是 k k k条,当 k k k为偶数时,我们可以直接分组,当 k k k为奇数时,偶数部分显然可以分一组,但剩余的边呢?剩余的边我们不妨留给父亲节点来分组,这样就有一个方法,设 d p [ u ] dp[u] dp[u]为 u u u为根的子树内能有多少种分法,并且分配完可能有剩余的边。当我们在 u u u的可用边为 k k k时,当 k k k为偶数,就有
d p [ u ] = f ( k ) × Π v ∈ s o n u d p [ v ] dp[u]=f(k)\times\Pi_{v\in son_{u}}dp[v] dp[u]=f(k)×Πv∈sonudp[v]
此时 u u u不留边给父亲,其中 f ( x ) f(x) f(x)为 x x x个元素两两分组的分法有多少种
当 k k k为奇数时
d p [ u ] = f ( k − 1 ) × Π v ∈ s o n u d p [ v ] dp[u]=f(k-1)\times\Pi_{v\in son_{u}}dp[v] dp[u]=f(k−1)×Πv∈sonudp[v]
此时留一条边给父亲使用,那么对 k k k有
k = 1 + ∑ v ∈ s o n u k v k=1+\sum_{v\in son_{u}}k_{v} k=1+v∈sonu∑kv
k v k_v kv是儿子给 v v v节点留给父亲的边数
对于 f ( x ) f(x) f(x),我们不妨这样考虑,先对 x x x个元素全排列,然后按顺序放入 1 1 1到 x 2 \frac{x}{2} 2x组,组内没有先后顺序,所以每个组需要除 2 ! 2! 2!,组和组之间也没有先后顺序,所以总体还要除以 ( x 2 ) ! (\frac{x}{2})! (2x)!,于是
f
(
x
)
=
x
!
(
2
!
)
x
2
×
(
x
2
)
!
f(x)=\frac{x!}{(2!)^{\frac{x}{2}}\times (\frac{x}{2})!}
f(x)=(2!)2x×(2x)!x!
预处理阶乘,阶乘逆元,2的幂,2的幂的逆元即可
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=100005,inf=0x3fffffff;
const long long INF=0x3f3f3f3f3f3f,mod=998244353;
ll qpow(ll a,ll b)
{
ll ret=1,base=a;
while(b)
{
if(b&1) ret=ret*base%mod;
base=base*base%mod;
b>>=1;
}
return ret;
}
ll inv(ll x){return qpow(x,mod-2);}
const long long inv2=inv(2);
struct way
{
int to,next;
}edge[N<<1];
int cnt,head[N],in[N];
void add(int u,int v)
{
edge[++cnt].to=v;
edge[cnt].next=head[u];
head[u]=cnt;
in[u]++;
}
int n,sum[N];
ll ans=1,fac[N],pw[N],invfac[N],invpw[N],dp[N];
//x个元素两两分组有多少种分法
ll calc(int x){return fac[x]*invpw[x/2]%mod*invfac[x/2]%mod;}
int dfs(int u,int fa)
{
//dp[u]->分配这棵子树有多少种方法?
sum[u]=dp[u]=1;
int tot=1;
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(v==fa) continue;
tot+=dfs(v,u);
sum[u]+=sum[v];
dp[u]=dp[u]*dp[v]%mod;
// if(sum[v]%2==0) dp[u]=dp[u]*(in[v]-1)%mod;
}
dp[u]=dp[u]*calc(tot%2?tot-1:tot)%mod;
return tot%2;
}
//n个点,n-1条边,n是奇数
//把n-1条边分为(n-1)个集合,每个集合有两条边,这两条边有公共点
//有多少种分法?
int main()
{
cin>>n;
for(int i=fac[0]=pw[0]=invfac[0]=invpw[0]=1;i<N;i++)
{
fac[i]=fac[i-1]*i%mod;
pw[i]=pw[i-1]*2%mod;
}
invfac[N-1]=inv(fac[N-1]);
invpw[N-1]=inv(pw[N-1]);
for(int i=N-2;i>=1;i--)
{
invfac[i]=invfac[i+1]*(i+1)%mod;
invpw[i]=invpw[i+1]*2%mod;
}
for(int i=1;i<n;i++)
{
int u,v; scanf("%d%d",&u,&v);
add(u,v); add(v,u);
}
dfs(1,0);
cout<<dp[1];
return 0;
}