P6596 How Many of Them 做题记录
P6596 How Many of Them 做题记录
Description
在无向连通图中,若一条边被删除后,图会分成不连通的两部分,则称该边为割边。
求满足如下条件的无向连通图的数量:
-
由 \(n\) 个结点构成,结点有标号。
-
割边不超过 \(m\) 条。
-
没有重边和自环。
答案对 \(10^{9}+7\) 取模。\(2≤n≤50,0≤m≤\dfrac{n(n-1)}{2}\)。
P6596 How Many of Them - 洛谷 (luogu.com.cn)
Solution
对于一个无向图,我们将其所有的 E-DCC 缩为一个点,那么整个无向图就会变为一棵树。于是我们把图计数转化为了树计数。
设 \(f_{i,j}\) 为 \(i\) 个点,\(j\) 条割边的无向连通图个数。
由于 \(n\) 个点的无向连通图的割边数量 \(\leq n-1\),则答案为 \(\sum_{i=0}^{\min(m,n-1)}f_{n,i}\)。
当 \(j>0\) 时,考虑 \(1\) 号节点所在的 E-DCC。设其含有 \(k\) 个点,删去它后剩下 \(x\) 个连通块,则答案可分为三部分:
- 这个 E-DCC 的数量,为 \(\binom{i-1}{k-1}\times f_{k,0}\);
- 剩下 \(i-k\) 个点,有 \(j-x\) 条割边,组成 \(x\) 个连通块的方案数,暂且记为 \(g_{i-k,j-x,x}\);
- 两者之间的边的选择方案。考虑剩下的每个连通块,设其大小为 \(y\)。其向 E-DCC 连的边,在 E-DCC 上的端点有 \(k\) 种选择,在连通块中的端点有 \(y\) 种选择。前面部分是好算的,为 \(k^x\);而后半部分我们要更改 \(g\) 的定义:将 “连通块” 改为 “有根连通块”。
于是我们得到:
接下来解决遗留问题 \(f_{i,0}\) 与 \(g_{i,j,k}\)。
首先考虑 \(f_{i,0}\) 的值,即大小为 \(i\) 的边双连通图数量。
考虑容斥,我们用大小为 \(i\) 的没有限制的连通图个数减去 E-DCC 个数大于 \(1\) 的连通图个数。
仍然考虑 \(1\) 号点所在的 E-DCC,设其大小为 \(j\),删掉它后剩下 \(k\) 个连通块。
类似 \(f\) 的转移,设 \(h_{i,j}\) 为 \(i\) 个点组成 \(j\) 个有根连通块的方案数,\(w_i\) 为 \(i\) 个点的连通图个数,则
不难得出,
然后再考虑 \(g_{i,j,k}\) 的值。设 \(1\) 号点所在的连通块有 \(p\) 个点,\(q\) 条割边。可以得到:
至此,我们求出了所有需要用到的值。
时间复杂度 \(O(n^5)\),瓶颈在于 \(g\) 的计算。实现中注意转移顺序。
signed main(){
read(n),read(m);
Init();
w[1]=1;
for(int i=2;i<=n;i++){
w[i]=pw[i*(i-1)>>1];
for(int j=1;j<i;j++)
(w[i]+=mod-w[j]*C(i-1,j-1)%mod*pw[(i-j)*(i-j-1)>>1]%mod)%=mod;
}
h[0][0]=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
for(int k=1;k<=i;k++)
(h[i][j]+=w[k]*C(i-1,k-1)%mod*h[i-k][j-1]%mod*k)%=mod;
}
}
g[0][0][0]=1;
for(int i=1;i<=n;i++){
f[i][0]=w[i];
for(int j=1;j<i;j++){
for(int k=1;k<=i-j;k++)
(f[i][0]+=mod-f[j][0]*C(i-1,j-1)%mod
*h[i-j][k]%mod*QuickPow(j,k)%mod)%=mod;
}
for(int j=1;j<i;j++){
for(int k=1;k<i;k++){
for(int x=1;x<=min(i-k,j);x++)
(f[i][j]+=f[k][0]*C(i-1,k-1)%mod*
g[i-k][j-x][x]%mod*QuickPow(k,x))%=mod;
}
}
for(int j=0;j<i;j++){
for(int k=1;k<=i;k++){
for(int p=1;p<=i;p++){
for(int q=0;q<=j;q++)
(g[i][j][k]+=f[p][q]*C(i-1,p-1)%mod*g[i-p][j-q][k-1]%mod*p)%=mod;
}
}
}
}
ll ans=0;
for(int i=0;i<=min(n-1,m);i++)
(ans+=f[n][i])%=mod;
printf("%lld\n",ans);
return 0;
}

浙公网安备 33010602011771号