[计数DP]How Many Of Them
How Many of Them
题目描述
在无向连通图中,若一条边被删除后,图会分成不连通的两部分,则称该边为割边。
求满足如下条件的无向连通图的数量:
-
由 \(n\) 个结点构成,结点有标号。
-
割边不超过 \(m\) 条。
-
没有重边和自环。
答案对 \(10^{9}+7\) 取模。
输入格式
仅一行,两个整数 \(n\) 和 \(m\)。
输出格式
一个整数,表示答案。
样例 #1
样例输入 #1
3 3
样例输出 #1
4
样例 #2
样例输入 #2
5 1
样例输出 #2
453
提示
\(2≤n≤50,0≤m≤\dfrac{n(n-1)}{2}\)
Source: Gennady Korotkevich (tourist), ITMO University.
做这题前可以做一做连通图Connected Graph
这道题思想很好,运用容斥原理,找到\(1\)为基准点,由题目,我们可以想到设\(F_{i,j}\)为有\(i\)个点的无向图中有\(j\)条割边,
如何转移呢,连通图Connected Graph这道题,我们知道包含1且\(i\)个点组成的无向图有\(h_i=2^{\frac{i(i-1)}{2}}-\sum_{j=1}^{i-1}h_j\times C_{i-1}^{j-1}\times 2^{\frac{(i-j)(i-j-1)}{2}}\),但是本题多加一条限制,最多\(M\)条割边
所以需要减去\(1~i-1\)条割边的情况\(f_{i,0}=h_i-\sum_{j=1}^{i-1}f_{i,j}\)
那如何先求出\(f_{i,j}\)呢,我们假设\(1\)这个点所在的连通块包含的点有\(k\)个,则有\(f_{k,0}\times C_{i-1}^{k-1}\)种情况,设\(g_{i,j,x}为i个点j条割边,x个连通块的方案数\),则除去\(1\)所在的连通块,还有\(\sum_{x=1}^{min(i-k,j)} g_{i-k,j-x,x}\)种情况,因为\(1\)这个点所在的连通块包含的\(k\)个点都有可能与\(x\)个连通块相连,所以再乘上\(k^x\)
即为$$f_{i,j}=\sum_{k=1}^{i-1}f_{k,0}\times C_{i-1}^{k-1}\times \sum_{x=1}^{min(i-k,j)} g_{i-k,j-x,x}$$
好现在就剩下\(g\)如何求了
按照上面的思路,我们可以求出\(g_{i,j,k}=\sum_{p=1}^{i-1}\sum_{q=0}^{j}f_{p,q}\times C_{p-1}^{q-1}\times g_{i-p,j-q,k-1}\times p\)每个连通块多乘了一个\(p\),因为\(p\)个点都可能与\(g\)所构成的连通块相连
点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define speed() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
using namespace std;
const int mod=1e9+7;
int n,m;ll f[55][1505],C[1500][1500],h[55],g[55][55][55];
ll qpow(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1)ans=ans*a%mod;
b>>=1;
a=a*a%mod;
}
return ans;
}
int main()
{
speed();
cin>>n>>m;
for(int i=0;i<=50;i++)
{
C[i][i]=1;C[i][0]=1;
for(int j=1;j<=i;j++)
{
C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
}
}
h[1]=1;
for(int i=2;i<=n;i++)
{
h[i]=qpow(2,i*(i-1)/2);
for(int j=1;j<=i-1;j++)
{
h[i]-=h[j]*C[i-1][j-1]%mod*qpow(2,(i-j)*(i-j-1)/2)%mod;
h[i]=(h[i]%mod+mod)%mod;
}
}
g[0][0][0]=1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=i-1;j++)
{
for(int k=1;k<=i-1;k++)
{
for(int x=1;x<=min(i-k,j);x++)
{
f[i][j]=(f[i][j]+f[k][0]*C[i-1][k-1]%mod*g[i-k][j-x][x]%mod*qpow(k,x))%mod;
}
}
}
f[i][0]=h[i];
for(int j=1;j<=i-1;j++)
{
f[i][0]-=f[i][j];
f[i][0]=(f[i][0]%mod+mod)%mod;
}
for(int j=0;j<i;j++)//atention
{
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]=(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,m);i++)
{
ans=(ans+f[n][i])%mod;
}
cout<<ans;
return 0;
}