Connected Graph

Connected Graph

求n个点的无向联通图数量,\(n\leq 50\)

直接无向联通图做状态等于是以边点做考虑,难以去重,考虑联通对立面即不联通。

不难求出n个点的总方案数为\(2^{\frac{n\times (n-1)}{2}}\),所以设\(f_i\)表示n个点的无向联通图个数,因此我们有

\[f_i=2^{\frac{n(n-1)}{2}}-\sum_{j=1}^{i-1}f_jC_i^j2^{\frac{(i-j)(i-j-1)}{2}}\]

但是这样的转移存在重复,考虑特殊化去重,注意到如果这张图不合法,可以等价于任何一个联通图不合法,于是可以强制让点1不合法,因此有

\[f_i=2^{\frac{n(n-1)}{2}}-\sum_{j=1}^{i-1}f_jC_{i-1}^{j-1}2^{\frac{(i-j)(i-j-1)}{2}}\]

边界:\(f_1=1\)

答案:\(f_n\)

时间复杂度显然\(O(n^2)\),但是高精度占去大量时间。

参考代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#define il inline
#define ri register
#define ll long long
using namespace std;
struct lll{
    int num[1001];
    lll(){num[0]=1;}
    il void clear(){
        memset(num,0,sizeof(num)),num[0]=1;
    }
    il void read(){
        string s;cin>>s,num[0]=s.size();
        for(ri int i(1);i<=num[0];++i)
            num[i]=s[num[0]-i];
    }
    il void print(){
        for(ri int i(num[0]);i;--i)putchar(num[i]+48);
    }
    il void operator=(string s){
        num[0]=s.size();
        for(ri int i(1);i<=s.size();++i)
            num[i]=num[s.size()-i];
    }
    il lll operator+(lll x){
        lll y;y.clear();ri int i;
        for(i=1;i<=num[0]||i<=x.num[0];++i){
            y.num[i]+=num[i]+x.num[i];
            if(y.num[i]>9)++y.num[i+1],y.num[i]-=10;
        }y.num[0]=i;
        while(!y.num[y.num[0]]&&y.num[0]>1)--y.num[0];
        return y;
    }
    il lll operator-(lll x){
        lll y;y.clear();ri int i;
        for(i=1;i<=num[0];++i){
            y.num[i]+=num[i]-x.num[i];
            if(y.num[i]<0)--y.num[i+1],y.num[i]+=10;
        }y.num[0]=i;
        while(!y.num[y.num[0]]&&y.num[0]>1)--y.num[0];
        return y;
    }
    il lll operator*(lll x){
        lll y;y.clear();ri int i,j,k;
        for(i=1;i<=num[0];++i){
            k=0;
            for(j=1;j<=x.num[0];++j)
                y.num[i+j-1]+=num[i]*x.num[j]+k,
                    k=y.num[i+j-1]/10,y.num[i+j-1]%=10;
            y.num[i+x.num[0]]+=k;
        }y.num[0]=i+j;
        while(!y.num[y.num[0]]&&y.num[0]>1)--y.num[0];
        return y;
    }
}p2[2501],c[51][51],dp[51];
int main(){
    int n,i,j;p2[0]="1";
    for(i=1;i<=2500;++i)p2[i]=p2[i-1]+p2[i-1];
    for(i=0;i<=50;++i){c[i][0]="1";
        for(j=1;j<=i;++j)
            c[i][j]=c[i-1][j]+c[i-1][j-1];
    }
    while(scanf("%d",&n),n){
        memset(dp,0,sizeof(dp)),dp[1]="1";
        for(i=2;i<=n;++i){
            dp[i]=p2[i*(i-1)>>1];
            for(j=1;j<i;++j)
                dp[i]=dp[i]-dp[j]*c[i-1][j-1]*p2[(i-j)*(i-1-j)>>1];
        }dp[n].print(),putchar('\n');
    }
    return 0;
}

转载于:https://www.cnblogs.com/a1b3c7d9/p/10987557.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值