JZOJ5351. 【NOIP2017提高A组模拟9.7】简单无向图 DP+组合数学

来源:互联网 发布:登录名无法访问数据库 编辑:程序博客网 时间:2024/06/11 05:24

题意:给出n个点和每个点的度数,问能有多少种组合方式形成一个简单无向图(多个联通块允许)。n<=2e3.

这题充分暴露出我的组合数学有多差。。
首先你需要手玩一段时间发现构造的图要不然是链要不然是环。
然后我们就可以按照情况DP了。
首先对于所有di=1的点,我肯定只能连成一条链。
那很好办了,设f[i][j]表示有i个度数为2的点,j个用来构造环,i-j个用来构造链的情况。
然后就是分类讨论&组合数学大法好。

情况1:新建一个环。
f[i][j]+=f[i3][j3](i2)(i1)/2
情况2:插入环中。
f[i][j]+=f[i1][j1](j1)
情况3:插入链中。
f[i][j]+=f[i1][j](ij1+tot1)
情况4:新建一条链
不存在的,链的条数在一开始就确定了(tot1/2),所以tot1为奇数方案数是0,这以后的操作只不过是在看是否能链条之间的互相插入而已。
注意把di=1之间的方案数算起来和f乘起来才是总方案数。

#include<cstdio>#include<algorithm>#include<cstring>#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;const int N=2e5+5;typedef long long ll;int a[N],n;const int mo=998244353;ll sum,ans,fac[N],tot1,tot2,f[2005][2005],g[N];int main(){    freopen("graph.in","r",stdin);    freopen("graph.out","w",stdout);    scanf("%d",&n);    fo(i,1,n)scanf("%d",&a[i]);    fo(i,1,n)    {        if (a[i]==1)tot1++;        else tot2++;    }    g[0]=1;    fo(i,2,tot1)g[i]=(g[i-2]+1ll*g[i-4]*(i-2)*(i-3)%mo)%mo;    ans=g[tot1]%mo;    tot1/=2;    f[0][0]=1;    fo(i,1,tot2)    {        fo(j,0,i)        {            if (tot1)f[i][j]=1ll*f[i-1][j]*(i-j-1+tot1)%mo;//line add            if (j>2)f[i][j]=(f[i][j]+1ll*f[i-3][j-3]*(i-2)*(i-1)/2%mo)%mo;//new loop;            if (j)f[i][j]=(f[i][j]+1ll*f[i-1][j-1]*(j-1)%mo)%mo;        }    }    fo(i,0,tot2)sum=(sum+f[tot2][i])%mo;    printf("%lld\n",ans*sum%mo);}