1. 程式人生 > 實用技巧 >《演算法競賽進階指南》0x5C計數類DP AcWing307n個點的連通無向圖數量

《演算法競賽進階指南》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; }