zoukankan      html  css  js  c++  java
  • POJ 1737 Connected Graph(高精度+DP递推)

    题面



    $ solution: $

    首先做个推销:带负数的压位高精度(加减乘+读写)

    然后:由 $ N $ 个节点组成的无向图的总数为: $ 2^{N imes (N-1)/2} $ (也就是说这个图总共有 $ N imes (N-1)/2 $ 条边,每一条边选或不选就可以得出来)

    然后我们直接开始分析题目,因为这道题需要求无向连通图的方案数,这道题似乎也不是一个结论题, $ wch $ 决定去找找规律,是不是 $ n $ 和 $ n-1 $ 有什么关系,但是 $ wch $ 发现他打不出表。 然后 $ wch $ 想到了分治合并,但如果将它分为两份 $ frac{n}{2} $ 似乎更不好合并了。但是他依旧觉得这题肯定可以用分开合并的方法(于是他觉得应该直接DP)。(说白了就是他比较傻,现在才想到直接DP)。

    然后他试图写出转移方程,然后他懵了。他发现很难不重不漏的把所有情况算进去(两个联通图暴力连边会导致重复),但是他发现如果两个联通图中间不连边就可以组成一个不连通图,于是他恍然大悟:似乎可以用所有图的方案数减去不连通的方案数!而一个不连通图一定有若干个连通图组成,我们可以围绕一个连通图来数方案,于是一个转移应运而生:我们钦定有 $ k $ 个节点在左边某个联通图里(可以用组合数选 $ k $ 个),剩下的 $ i-k $ 个节点在右边的图里,但是这样仔细一想也会重复。为什么呢?我们围绕的那个 $ k $ 个节点组成的连通图是不确定的,他有可能被后面的枚举中( $ i-k $ 所组成的图)取到。所以我们要把它固定下来,(在算法竞赛里称为找基准点,并围绕它构造一个不可划分的整体)于是我们钦定一号节点在左边那个连通图中,这样我们就能不重不漏的算下左右情况了!

    设 $ F[i] $ 表示有 $ i $ 个节点组成的联通图的个数有多少个,我们考虑这个怎么转移过来的:首先枚举左边的连通图的大小 $ 1<k<i $ ,然后我们要钦定一号结点在里面,所以我们只需要从剩下的 $ i-1 $ 个 节点里选出 $ k-1 $ 个即可(显然可以选 $ C^{k-1}_{i-1} $ 种),然后这 $ k $ 个节点是需要组成一个连通图的,所以再乘上一个 $ F[k] $ 然后我们还需要乘上后面的 $ i-k $ 个节点组成的任意图即: $ 2^{(i-k) imes (i-k-1)/2} $ 于是转移方程即为:

    $ F[i]=2^{i imes (i-1)/2}-sum^{i-1}_{k=1}{C^{k-1}_{i-1} imes F[k] imes 2^{(i-k) imes (i-k-1)/2}} $



    $ code: $

    #include<iostream>
    #include<cstdio>
    #include<iomanip>
    #include<algorithm>
    #include<cstring>
    #include<cstdlib>
    #include<ctime>
    #include<cmath>
    #include<vector>
    #include<queue>
    #include<map>
    #include<set>
    
    #define ll long long
    #define db double
    #define inf 0x7fffffff
    #define rg register int
    
    using namespace std;
    
    int n;
    
    struct gj{
    	
    	bool fu; //是否是负数
    	int tt,mod; //高精的长度
        int s[1005]; //压位用的数组
    	
    	inline gj(){ //整体初始化
    		fu=0; tt=0; mod=1e9;
    		memset(s,0,sizeof(s));
    	}
        
        inline gj read(){  register char ch; //高精度读入
    		while(!isdigit(ch=getchar()))if(ch=='-')fu=1;
    		char _[100005]; rg l=0,r=-1; _[0]=ch;
            while(isdigit(_[++l]=getchar()));; tt=l/9-!(l%9);
            for(rg i=(l-1)%9+1;i;--i) (s[tt]*=10)+=_[++r]^48;
            for(rg i=tt-1;i>=0;--i)for(rg j=0;j<9;++j)(s[i]*=10)+=(_[++r]^48);
    		while(tt&&!s[tt])--tt;; return (*this);
        }
        inline void print(){ //高精度输出
    		if(fu)putchar('-');
            printf("%d",s[tt]);
            for(rg i=tt-1;i>=0;--i)
                printf("%09d",s[i]);
            putchar('
    ');
        }
    
    	inline bool operator >(const gj &x){ //定义大于
    		if(tt!=x.tt)return tt>x.tt;
    		for(rg i=tt;i>=0;--i)
    			if(s[i]!=x.s[i])return s[i]>x.s[i];
    		return 0;
    	}
    	inline bool operator <(const gj &x){ //定义小于
    		if(tt!=x.tt)return tt<x.tt;
    		for(rg i=tt;i>=0;--i)
    			if(s[i]!=x.s[i])return s[i]<x.s[i];
    		return 0;
    	}
    
    	inline gj operator =(int x){ //int的等于
    		while(tt)s[tt]=0,--tt;
    		s[0]=x%mod; s[1]=x/mod;
    	    if(s[1])tt=1;; return *this;
    	}
    	inline gj operator =(ll x){ //int的等于
    		while(tt)s[tt]=0,--tt;
    		while(x)s[tt]=x%mod,x/=mod,++tt;
    	    if(!s[tt])--tt;; return *this;
    	}
    
    	inline void add(const gj &x){ //加法的底层
            rg sign=0; if(x.tt>tt)tt=x.tt;
            for(rg i=0;i<=tt;++i){
                s[i]+=x.s[i]+sign; sign=0;
                if(s[i]>mod)s[i]-=mod,sign=1;
            }if(sign)s[++tt]=1;
    	}
    
    	inline void cut(const gj &x){ //减法的底层
    		if(fu)cout<<54564<<endl;
            rg sign=0; 
            for(rg i=0;i<=tt;++i){
                s[i]-=x.s[i]+sign; sign=0;
                if(s[i]<0)s[i]+=mod,sign=1;
            }while(tt&&!s[tt])--tt;
    		if(!tt&&!s[tt]) fu=0;
    	}
    
    	inline void mul(const gj &x){ //乘法的底层
    		gj y; ll num; y.tt=tt+x.tt;
            for(rg i=0;i<=tt;++i){ num=0;
                for(rg j=0;j<=x.tt;++j){
                    num=(ll)s[i]*x.s[j]+y.s[j+i]+num;
                    y.s[j+i]=num%mod; num/=mod;
                } if(num)y.s[x.tt+i+1]=num;
            }if(num)++y.tt;; *this=y;
    	}
    
        inline void operator +=(gj x){ //赋值加法重载
    		if(fu==x.fu){(*this).add(x); return;}
    		if(*this>x) (*this).cut(x);
    		else x.cut(*this),*this=x,fu^=1;
        }
        inline gj operator +(const gj &x){ //加法正常重载
    		gj y=*this; y+=x; return y;
        }
    	
    	inline void operator -=(gj x){ //赋值减法重载
    		if(fu!=x.fu){(*this).add(x); return;}
    		if(*this>x){(*this).cut(x); return;}
    		x.cut(*this); *this=x; if(s[tt])fu^=1;
        }
        inline gj operator -(const gj &x){ //减法正常重载
    		gj y=*this; y-=x; return y;
        }
        
        inline void operator *=(gj x){ //赋值乘法重载
    		if(!s[tt]||!x.s[x.tt]){gj y; *this=y;}
    		if(fu!=x.fu)fu=1;else fu=0;; (*this).mul(x);
        }
        inline gj operator *(const gj &x){ //乘法正常重载
    		gj y=*this; y*=x; return y;
        }
    
    	inline gj operator *(int x){  gj y; y=x; return (*this)*y;}
    	inline void operator *=(int x){gj y; y=x; (*this)*=y;}
    	inline gj operator +(int x){  gj y; y=x; return (*this)+y;}
    	inline void operator +=(int x){gj y; y=x; (*this)+=y;}
    	inline gj operator -(int x){  gj y; y=x; return (*this)-y;}
    	inline void operator -=(int x){gj y; y=x; (*this)-=y;}
    }f[51],pf[1235],c[51][51];
    
    inline int qr(){
    	register char ch; register bool sign=0; rg res=0;
    	while(!isdigit(ch=getchar())) if(ch=='-')sign=1;
    	while(isdigit(ch)) res=res*10+(ch^48),ch=getchar();
    	return sign?-res:res;
    }
    
    int main(){
    	//freopen("in.in","r",stdin);
    	//freopen("out.out","w",stdout);
    	pf[0]=1; n=50;
    	for(rg i=0;i<=50;++i) c[i][0]=1;
    	for(rg i=0;i<=1225;++i) pf[i+1]=pf[i]*2;
    	for(rg i=1;i<=50;++i)
    		for(rg j=1;j<=50;++j)
    			c[i][j]=c[i-1][j]+c[i-1][j-1];
        f[1]=1; f[2]=1;
    	for(rg i=3;i<=n;++i){
    		f[i]=pf[i*(i-1)/2];
    		for(rg j=1;j<i;++j)
    			f[i]-=f[j]*c[i-1][j-1]*pf[(i-j)*(i-j-1)/2];
    	} while((n=qr()))f[n].print();
    	return 0;
    }
    
  • 相关阅读:
    Spring学习总结(3)——Spring配置文件详解
    Hadoop学习总结(1)——大数据以及Hadoop相关概念介绍
    华为云文字识别关键技术和特别需要注意的事宜
    如何不用BPM配置时间
    华为云DevCloud为开发者提供高效智能的可信开发环境
    【HC资料合集】2019华为全联接大会主题资料一站式汇总,免费下载!
    在modelarts上部署mask-rcnn模型
    独立物理机和虚拟机比较有什么优势?
    解惑Python模块学习,该如何着手操作...
    sar命令,linux中最为全面的性能分析工具之一
  • 原文地址:https://www.cnblogs.com/812-xiao-wen/p/10995815.html
Copyright © 2011-2022 走看看