《算法竞赛进阶指南》0x5C计数类DP AcWing307n个点的连通无向图数量
题目链接:
考虑拿掉点1时点2的情况,设此时点2所在连通块共k各点,这k个点以及剩下的n-k个点分别处在一个连通块中,
其方案数为F(k)*F(n-k),点2需在除去点1和2的点中取k-1个点构成连通块,故方案数为C(n-2,k-1),
而这总共k个点与剩下除去点1的n-k-1个点必须通过点1才能连通,即这k个点与点1至少有1条边连通,
这样的方案数为2^k-1,故这样的情况总共有F(k)*F(n-k)* C(n-2,k-1)*( 2^k-1)种。
因此可得递推公式为:
F(n)=Sum(F(k)*F(n-k)* C(n-2,k-1)*( 2^k-1) | 1<=k<n)。
反向来的话就是,所有的情况减掉不连通的情况,不连通的情况可以看点1有多少个点和它是在一个连通块中的,另外的可以随便排。
但是这种实现起来更加复杂了一点。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 60;
const int S = 600;
int n;
struct A{
int a[S],len;
inline A operator / (const int x)const {//大数除以一个整数
int num=0;
A res;
memset(res.a,0,sizeof res.a);
res.len=0;
for(int i=len;i;i--){
num=num*10+a[i];
res.a[i]=num/x;
num%=x;
if(!res.len && res.a[i])res.len=i;//第一位不是0的数
}
if(!res.len)res.a[1]=0,res.len=1;
return res;
}
inline A operator + (const A &x)const {
A ans;
memset(ans.a,0,sizeof ans.a);
for(int i=1;i<=max(len,x.len);i++){
ans.a[i]+=a[i]+x.a[i];
ans.a[i+1]=ans.a[i]/10;
ans.a[i]%=10;
}
ans.len=max(len,x.len);
if(ans.a[ans.len+1])ans.len++;
return ans;
}
inline A operator * (const A &x)const {
A ans;
memset(ans.a,0,sizeof ans.a);
for(int i=1;i<=len;i++)
for(int j=1;j<=x.len;j++){
ans.a[i+j-1]+=a[i]*x.a[j];
ans.a[i+j]+=ans.a[i+j-1]/10;
ans.a[i+j-1]%=10;
}
ans.len=len+x.len-1;
if(ans.a[ans.len+1])++ans.len;
return ans;
}
inline A operator - (const A &x)const {
A ans;
ans.len=len;
for(int i=1;i<=len;i++)ans.a[i]=a[i];
for(int i=1;i<=x.len;i++){
ans.a[i]-=x.a[i];
if(ans.a[i] < 0)ans.a[i]+=10;
ans.a[i+1]--;
}
while(!ans.a[ans.len])ans.len--;
return ans;
}
}f[N],p[N];
inline A C(int x,int y){
A ans;
ans.len=ans.a[1]=1;
for(int i=y,j=1;j<=x;i--,j++){
int t=i;
A tmp;
tmp.len=0;
while(t){
tmp.a[++tmp.len]=t%10;
t/=10;
}
ans=ans*tmp/j;//任意连续n个自然数的积一定是[1,n]的倍数
}
return ans;
}
inline void print(A &x){
for(int i=x.len;i;i--)printf("%d",x.a[i]);
cout<<endl;
}
int main(){
for (int i = 1; i <= 50; i++) {
ll t = (1ll << i) - 1;
while (t) {
p[i].a[++p[i].len] = t % 10;
t /= 10;
}
}
f[1].len = f[2].len = f[1].a[1] = f[2].a[1] = 1;
for (int i = 3; i <= 50; i++)
for (int j = 1; j <= i - 1; j++)
f[i] = f[i] + C(j - 1, i - 2) * f[j] * f[i-j] * p[j];
while (cin >> n && n) print(f[n]);
return 0;
}

![《算法竞赛进阶指南》0x5C计数类DP AcWing307n个点的连通无向图数量
[编程语言教程]](https://www.zixueka.com/wp-content/uploads/2024/01/1706714054-cdbe23409d1f182.jpg)
