zoukankan      html  css  js  c++  java
  • 《算法竞赛进阶指南》0x5C计数类DP AcWing307n个点的连通无向图数量(bigint)

    题目链接: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;    
    }
    
    
     
  • 相关阅读:
    【剑指offer】面试题35:第一个只出现一次的字符
    【剑指offer】面试题34:丑数
    【剑指offer】面试题33:把数组排成最小的数
    【剑指offer】面试题32:从1到n整数中1出现的次数
    【剑指offer】面试题31:连续子数组的最大和
    【剑指offer】面试题30:最小的 k 个数
    【剑指offer】面试题29:数组中出现次数超过一半的数字
    【剑指offer】面试题28:字符串的排列
    【剑指offer】面试题27:二叉搜索树与双向链表
    【剑指offer】面试题26:复杂链表的复制
  • 原文地址:https://www.cnblogs.com/randy-lo/p/13445623.html
Copyright © 2011-2022 走看看