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

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); 
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值