题目链接:https://www.acwing.com/problem/content/description/309/
方法一:
考虑拿掉点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; }
方法二代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int N = 60; const int S = 600; const int base = 10000; int n; struct A{ int a[S],len; A(){//常规初始化 memset(a,0,sizeof a); len=0; } A(int x){//用一个int数初始化一个大数 A(); len=0; while(x){ a[++len]=x%base; x/=base; } } inline void print(){ printf("%d",a[len]); for(int i=len-1;i;i--){ if(a[i]<10)printf("000"); else if(a[i]<100)printf("00"); else if(a[i]<1000)printf("0"); printf("%d",a[i]); } cout<<endl; } }f[N],p[1500],c[N][N]; inline A operator + (const A &a,const A &b){ A ans; ans.len=max(a.len,b.len); for(int i=1;i<=max(a.len,b.len);i++){ ans.a[i]+=a.a[i]+b.a[i]; ans.a[i+1]=ans.a[i]/base; ans.a[i] %=base; } if(ans.a[ans.len+1])ans.len++; return ans; } inline A operator / (const A &a,const int &b){ A ans; int num=0; for(int i=a.len;i;i--){ num=num*base+a.a[i]; ans.a[i]=num/b; num%=b; if(ans.len==0 && ans.a[i])ans.len=i; } return ans; } inline A operator * (const A &a,const int &b){ A ans=a; for(int i=ans.len;i;i--)ans.a[i]*=b; for(int i=1;i<=ans.len;i++){ ans.a[i+1]+=ans.a[i]/base; ans.a[i]%=base; } while(ans.a[ans.len+1]){ ans.len++; ans.a[ans.len+1]+=ans.a[ans.len]/base; ans.a[ans.len]%=base; } return ans; } inline A operator * (const A &a,const A &b){ A ans; ans.len=a.len+b.len-1; for(int i=1;i<=a.len;i++) for(int j=1;j<=b.len;j++){ ans.a[i+j-1]+=a.a[i]*b.a[j]; ans.a[i+j]+=ans.a[i+j-1]/base; ans.a[i+j-1]%=base; } while(ans.a[ans.len+1])ans.len++; return ans; } inline A operator - (const A &a,const A &b){//保证减法完成之后是正数 A ans=a; for(int i=1;i<=b.len;i++){ ans.a[i]-=b.a[i]; if(ans.a[i]<0){ ans.a[i]+=base; ans.a[i+1]--; } } while(ans.a[ans.len]==0)ans.len--; return ans; } inline A C(int x,int y){ A ans=A(1); for(int i=y,j=1;j<=x;i--,j++){ ans=ans*i/j;//任意连续n个自然数的积一定是[1,n]的倍数 } return ans; } int main(){ p[0]=A(1); p[1]=A(2); for(int i=2;i<=1300;i++){ p[i]=p[i-1]*p[1]; } c[0][0]=A(1); for(int i=1;i<=50;i++){ c[i][0]=A(1); for(int j=1;j<=i;j++){ c[i][j]=c[i-1][j-1]+c[i-1][j]; } } f[1]=A(1); f[2]=A(1); for(int i=3;i<=50;i++){ f[i]=p[i*(i-1)/2]; for(int j=1;j<=i-1;j++){ f[i]=f[i]-c[i-1][j-1]*f[j]*p[(i-j)*(i-j-1)/2]; } } while(cin>> n && n)f[n].print(); return 0; }