FZOJ 4344 连通性
称前 \(n-m\) 个点为黑点,后面 \(m\) 个点为白点。题意就是不能有任意两个点,满足他们之间的每条路径上都有白点(除这两个端点)。
发现如果有些白点与黑点之前不连边,那么这些白点之间必然是一些团。方案数就是第二类斯特林数,这些白点我们先不管。
记 \(g_i\) 表示 \(i\) 个点的连通图的方案数,根据简单容斥得(下面减一的意思是固定 \(1\) 号点在第一个连通块):
\[g[i]=2^{\frac{i\times (i-1)}2}-\sum\limits_{j=1}^{i-1}g[j]\times\binom{i-1}{j-1}\times 2^{\frac{(i-j)(i-j-1)}{2}}
\]
与黑点相连的这些白点,他们必然只能连向某一个黑点的连通块,记 \(ans[i][j]\) 表示 \(i\) 个黑点 \(j\) 个白点组成的连通块的方案数(不包括那些团)。
\[ans[i+k][j+l]+=ans[i][j]\times \binom {i+k-1}{k-1}\times \binom{j+l}{j}\times h[i][j]
\]
其中 \(h[i][j]=g[i]\times 2^{\frac{j\times(j-1)}2}\times (2^i-1)^j\)。
那么记 \(Ans[i][j]\) 表示 \(i\) 个黑点,\(j\) 个白点,这些白点中包括那些团的方案数。合并一下就好。
\[Ans[i][j+k]+=ans[i][j]\times \binom{j+k}{k}\times 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\n",(Ans[x-y][y]+M)%M);
}
}
signed main()
{
init();
work();
return 0;
}
由于博主比较菜,所以有很多东西待学习,大部分文章会持续更新,另外如果有出错或者不周之处,欢迎大家在评论中指出!