返回顶部

[计数DP]How Many Of Them

How Many of Them

题目描述

在无向连通图中,若一条边被删除后,图会分成不连通的两部分,则称该边为割边。

求满足如下条件的无向连通图的数量:

  1. \(n\) 个结点构成,结点有标号。

  2. 割边不超过 \(m\) 条。

  3. 没有重边和自环。

答案对 \(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;
}
posted @ 2024-06-18 11:34  wlesq  阅读(46)  评论(2编辑  收藏  举报