【NOIP2017提高A组模拟9.7】简单无向图

来源:互联网 发布:pc使用mac虚拟机下载 编辑:程序博客网 时间:2024/05/22 08:08

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); }
原创粉丝点击