FZOJ 4344 連通性
阿新 • • 發佈:2020-09-06
稱前 \(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; }