Description
给出一张n个点的简单图,和每个点的度数di,求这样的图的个数。
n<=2000,di=1,2
Solution
既然度数只有1,2两种,那么显然这种图中只有环和链。
但是这个环有要求大小>=3,因为简单图不能有重边。
很好想到把环和链分开考虑,链的个数是确定的。
那么我们设si,j表示i个2,放到j个环中的方案数,
如果没有考虑环的大小限制s显然是斯特林数,
但是有限制我们就要考虑减重,斯特林数的转移是每次放一个大小为1的环
那我们就每次放一个大小为3的环,并且保证i一定在环中就不会有重复了!!
接下来设fi,j表示i个2,放到j条链中的方案数,
这个也是类似上面的转移,就是系数不太一样,也不用减重。
不过因为我们可以一条链可以不用放2,所以我们最后还要Fi=∑fi,j*Pcnt,j来考虑不放的情况
其中P表示排列数,cnt表示环的个数。
最后把环和链组合在一起就好了。
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int N=2*1e3+5,mo=998244353;
int n,x,ans,one,two,fact[N],inv[N],f[N][N],s[N][N],S[N],F[N];
int pwr(int x,int y) {
int z=1;
for(;y;y/=2,x=(ll)x*x%mo)
if (y&1) z=(ll)z*x%mo;
return z;
}
void C_pre() {
fact[0]=1;fo(i,1,n) fact[i]=(ll)fact[i-1]*i%mo;
inv[0]=1;inv[n]=pwr(fact[n],mo-2);fd(i,n-1,1) inv[i]=(ll)inv[i+1]*(i+1)%mo;
}
int C(int m,int n) {
return (ll)fact[m]*inv[n]%mo*inv[m-n]%mo;
}
int main() {
freopen("graph.in","r",stdin);
freopen("graph.out","w",stdout);
scanf("%d",&n);
fo(i,1,n) {
scanf("%d",&x);
if (x==1) one++;else two++;
}
if (one&1) {
puts("0");
return 0;
}
ans=1;fo(i,1,one/2) ans=(ll)ans*(2*i-1)%mo;
C_pre();
s[0][0]=1;
fo(j,1,two)
fo(i,3,two) {
s[i][j]=(ll)s[i-1][j]*(i-1)%mo;
(s[i][j]+=(ll)s[i-3][j-1]*C(i-1,2)%mo)%=mo;
}
f[0][0]=1;
fo(j,1,one/2)
fo(i,1,two) {
f[i][j]=(ll)f[i-1][j]*(i+j-1)%mo;
(f[i][j]+=f[i-1][j-1])%=mo;
}
fo(i,0,two)
fo(j,0,two)
(S[i]+=s[i][j])%=mo;
fo(i,0,two)
fo(j,0,one/2)
(F[i]+=(ll)f[i][j]*C(one/2,j)%mo*fact[j]%mo)%=mo;
int res=0;
fo(i,0,two) (res+=(ll)C(two,i)*S[i]%mo*F[two-i]%mo)%=mo;
ans=(ll)ans*res%mo;
printf("%d\n",ans);
}