zoukankan      html  css  js  c++  java
  • FZOJ 4344 连通性

    称前 (n-m) 个点为黑点,后面 (m) 个点为白点。题意就是不能有任意两个点,满足他们之间的每条路径上都有白点(除这两个端点)。

    发现如果有些白点与黑点之前不连边,那么这些白点之间必然是一些团。方案数就是第二类斯特林数,这些白点我们先不管。

    (g_i) 表示 (i) 个点的连通图的方案数,根据简单容斥得(下面减一的意思是固定 (1) 号点在第一个连通块):

    [g[i]=2^{frac{i imes (i-1)}2}-sumlimits_{j=1}^{i-1}g[j] imesinom{i-1}{j-1} imes 2^{frac{(i-j)(i-j-1)}{2}} ]

    与黑点相连的这些白点,他们必然只能连向某一个黑点的连通块,记 (ans[i][j]) 表示 (i) 个黑点 (j) 个白点组成的连通块的方案数(不包括那些团)。

    [ans[i+k][j+l]+=ans[i][j] imes inom {i+k-1}{k-1} imes inom{j+l}{j} imes h[i][j] ]

    其中 (h[i][j]=g[i] imes 2^{frac{j imes(j-1)}2} imes (2^i-1)^j)

    那么记 (Ans[i][j]) 表示 (i) 个黑点,(j) 个白点,这些白点中包括那些团的方案数。合并一下就好。

    [Ans[i][j+k]+=ans[i][j] imes inom{j+k}{k} imes S_2[k][0] ]

    其中 (S_2[][0]) 表示第二类斯特林数一行的和。

    代码:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<iostream>
    #define int long long
    
    using namespace std;
    
    const int N=109,M=1000000007;
    int fac[N],inv_fac[N],h[N][N],ans[N][N],Ans[N][N],g[N],S2[N][N];
    
    int ksm(int a,int b)
    {
    	int res=1;
    	while(b)
    	{
    		if(b&1)
    			res=res*a%M;
    		b>>=1,a=a*a%M;
    	}
    	return res;
    }
    
    int binom(int n,int m)
    {
    	if(n<m) return 0;
    	return fac[n]*inv_fac[n-m]%M*inv_fac[m]%M;
    }
    
    void init()
    {
    	fac[0]=1;
    	for (int i=1;i<=100;i++)
    		fac[i]=fac[i-1]*i%M;
    	inv_fac[100]=ksm(fac[100],M-2);
    	for (int i=99;i>=0;i--)
    		inv_fac[i]=inv_fac[i+1]*(i+1)%M;
    	g[0]=0;
    	for (int i=1;i<=100;i++)
    	{
    		g[i]=ksm(2,i*(i-1)/2);
    		for (int j=1;j<i;j++)
    			g[i]=(g[i]-g[j]*ksm(2,(i-j)*(i-j-1)/2)%M*binom(i-1,j-1)%M)%M;
    	}
    	for (int i=0;i<=100;i++)
    		for (int j=0;j<=100;j++)
    			h[i][j]=g[i]*ksm(2,(j==1?0:(j-1)*j/2))%M*ksm((ksm(2,i)-1),j)%M;
    	S2[0][0]=1;
    	for (int i=1;i<=100;i++)
    		for (int j=1;j<=i;j++)
    			S2[i][j]=(S2[i-1][j-1]+j*S2[i-1][j]%M)%M;
    	for (int i=1;i<=100;i++)
    		for (int j=1;j<=i;j++)
    			S2[i][0]=(S2[i][0]+S2[i][j])%M;
    }
    
    void work()
    {
    	ans[0][0]=1;
    	for (int n=0;n<=100;n++)
    		for (int m=0;m<=100;m++)
    			for (int i=1;i+n<=100;i++)
    				for (int j=0;j+m<=100;j++)
    					ans[n+i][m+j]=(ans[n+i][m+j]+binom(n+i-1,i-1)*binom(m+j,j)%M*h[i][j]%M*ans[n][m]%M)%M;
    	for (int n=0;n<=100;n++)
    		for (int m=0;m<=100;m++)
    			for (int i=0;i+m<=100;i++)
    				Ans[n][m+i]=(Ans[n][m+i]+binom(m+i,i)*ans[n][m]%M*S2[i][0]%M)%M;
    	int T,x,y;
    	scanf("%lld",&T);
    	while(T--)
    	{
    		scanf("%lld %lld",&x,&y);
    		printf("%lld
    ",(Ans[x-y][y]+M)%M);
    	}
    }
    
    signed main()
    {
    	init();
    	work();
    	return 0;
    }
    
    
  • 相关阅读:
    转职游戏策划第四周
    转职游戏策划第三周
    HTML表单验证库SMValidator
    win7通过easyBCD引导ubuntu
    SWF混淆工具(智能提取需要混淆的字段)
    图文混排组件(RichTextField)
    windows下gvim使用vundle插件
    AirMVC
    再战中原之地图编辑器
    再战中原之菜单系统
  • 原文地址:https://www.cnblogs.com/With-penguin/p/13621225.html
Copyright © 2011-2022 走看看