【JZOJ5870】地图
Description
给定N个度数为1或2的点,求所有带标号简单无向图(无重边和自环)的方案数。
Solution
设度数为2的点有
n
n
n个,度数为1的有
m
m
m个。
因为只有1和2,所以图一定是由许多链和大小大于等于3的简单环构成的,于是我们可以先将度数为1的点忽略(它们只能构成链的两端,一定是两两配对的)。
先考虑环,设
F
i
F_i
Fi表示用
1
1
1~
i
i
i的点组成若干个环的方案数,考虑转移,枚举新加入环的大小,由于是带标号的,我们强制把
i
i
i放进新的环内,使得每个环具有区分性(可以理解为给每个环编号),避免算重。转移大概是这样:
F
i
=
∑
j
=
3
i
1
2
F
i
−
j
C
i
−
1
j
−
1
(
j
−
1
)
!
F_i=\sum_{j=3}^i\dfrac{1}{2}F_{i-j}C_{i-1}^{j-1}(j-1)!
Fi=∑j=3i21Fi−jCi−1j−1(j−1)!
然后处理
G
i
G_i
Gi,先设一个
g
i
,
j
g_{i,j}
gi,j表示
i
i
i个点组成
j
j
j条链的方案数。转移易得为
g
i
,
j
=
g
i
−
1
,
j
⋅
(
i
−
1
+
j
)
+
g
i
−
1
,
j
−
1
g_{i,j}=g_{i-1,j}\cdot (i-1+j)+g_{i-1,j-1}
gi,j=gi−1,j⋅(i−1+j)+gi−1,j−1。最后考虑编号:
G
i
=
∑
g
i
,
j
⋅
P
m
/
2
j
G_i=\sum g_{i,j}\cdot P_{m/2}^j
Gi=∑gi,j⋅Pm/2j
最后求出
a
n
s
=
(
∏
i
=
1
m
/
2
2
i
−
1
)
∑
i
=
0
n
F
i
⋅
G
n
−
i
ans=(\prod_{i=1}^{m/2}2i-1)\sum_{i=0}^n F_i\cdot G_{n-i}
ans=(∏i=1m/22i−1)∑i=0nFi⋅Gn−i
Code
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;++i)
#define fd(i,j,k) for(int i=j;i>=k;--i)
using namespace std;
typedef long long ll;
const int N=2e3+10,mo=998244353,n2=499122177;
int cn[N];
ll g[N][N],c[N][N];
ll F[N],G[N],jc[N];
int main()
{
freopen("map.in","r",stdin);
freopen("map.out","w",stdout);
int n,o;
scanf("%d",&n);
fo(i,1,n) scanf("%d",&o),cn[o]++;
if(cn[1]&1) return printf("0"),0;
c[0][0]=jc[0]=1;
fo(i,1,n){
c[i][0]=1;
fo(j,1,i) c[i][j]=(c[i-1][j]+c[i-1][j-1])%mo;
jc[i]=jc[i-1]*i%mo;
}
F[0]=1;
fo(i,3,cn[2])
fo(j,3,i) F[i]=(F[i]+F[i-j]*c[i-1][j-1]%mo*jc[j-1]%mo*n2%mo)%mo;
g[0][0]=1;
fo(i,1,cn[2])
fo(j,1,min(cn[1]/2,i)) g[i][j]=(g[i-1][j]*(i+j-1)%mo+g[i-1][j-1])%mo;
G[0]=1;
fo(i,1,cn[2])
fo(j,1,cn[1]/2) G[i]=(G[i]+g[i][j]*c[cn[1]/2][j]%mo*jc[j]%mo)%mo;
ll ans=0;
fo(i,0,cn[2]) ans=(ans+c[cn[2]][i]*G[i]%mo*F[cn[2]-i]%mo)%mo;
ll tmp=1;
fo(i,2,cn[1]/2) tmp=tmp*(i+i-1)%mo;
printf("%lld",ans*tmp%mo);
}