Connected Graph

题解 Connected Graph

楼教主“男人八题”之首。集训的时候老师讲“简单”DP的时候讲到了这道题,但并没有提及这就是男人八题之一。在凌晨一点一遍A,感觉不是一般的爽(≧∇≦)ノ

这道题比较猛,爆搜的复杂度是\(O(2^{\frac {n\times(n-1)}{2}})\),复杂度将以平方的指数级大爆炸,基本上没有写的必要。那就直接讲正解

\[f(n)\quad\quad表示大小为n的标号联通图的个数 \]

\[g(n)\quad\quad表示大小为n的标号不连通图的个数 \]

首先是一个很比较显然的转换公式

\[f(n)=2^{\frac{n\times(n-1)}{2}}-g(n) \]

解释一下:大小为n的标号图的个数为\(2^{\frac{n\times(n-1)}{2}}\),即枚举每一条边的情况。大小为n的标号图必然可以分为联通不连通两种,因此得到上述转换公式

讲题面要求转换之后,尝试对\(g(n)\)进行求解。

枚举节点 1 所在的联通块大小k,记当前情况为ans。由于 1 处于大小为 k 的联通块中,因此方案数为 f(k) ;又由于图是编号的,因此从除 1 以外的 n-1 个节点中抽 k-1 个节点,作为联通块里的节点,方案数为 C(k-1)(n-1) ;剩下的节点没有任何要求,方案数:\(2^{\frac{(n-k)\times(n-k-1)}{2}}\);因此总方案数:\(ans=f(k)\times C_{n-1}^{k-1}\times 2^{\frac{(n-k)\times(n-k-1)}{2}}\)

\[g(n)=\sum_{i=1}^{n-1} f(i)\times C_{n-1}^{i-1}\times 2^{\frac{(n-i)\times(n-i-1)}{2}} \]

再结合转化公式,我们即可\(O(n^2)\)求出所有的f(n)φ(゜▽゜*)♪

好了,我们去题面找一找模数就可以开始码题了..找一找..emm..?没有模数?是的,这道题需要手写高精度!而且实测表明,f[50]是一个369位的大数字,再考虑上乘法的进位,实际上我开了1000位( ̄▽ ̄)"

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
#define lor(a,b,c) for(register int a=b;a<=c;++a)
#define ror(a,b,c) for(register int a=c;a>=b;--a)

struct data{
	static const int MAX=1005;
	int n,num[MAX]; bool nega;

	data(){
		n=0; nega=false; lor(i,0,1000) num[i]=0;
	}

	void print(){
		for(int i=n;i>=1;--i) printf("%d",num[i]);
	}

	void init(string a){
		n=a.length();
		for(int i=n;i>=1;--i) num[i]=a[n-i]-'0';
	}

	void init(int p){
		while(p) {num[++n]=p%10; p/=10;}
	}

	data operator + (data &x){
		data ans;
		ans.n=max(n,x.n);
		lor(i,1,ans.n) ans.num[i]=num[i]+x.num[i];
		lor(i,1,ans.n) ans.num[i+1]+=ans.num[i]/10,ans.num[i]%=10;
		if(ans.num[ans.n+1]) ans.n=ans.n+1;
		return ans;
	}

	data operator - (data &x){
		data ans;
		lor(i,1,n){
			if(num[i]<x.num[i]) num[i+1]--,num[i]+=10;
			ans.num[i]=num[i]-x.num[i];
		}
		ror(i,1,1000) if(ans.num[i]) {ans.n=i; break;}
		return ans;
	}

	data operator * (data &x){
		data ans;
		lor(i,1,n) lor(j,1,x.n) ans.num[i+j-1]+=num[i]*x.num[j];
		lor(i,1,n+x.n) ans.num[i+1]+=ans.num[i]/10,ans.num[i]%=10;
		ror(i,0,1000) if(ans.num[i]) {ans.n=i; break;}
		return ans;
	}

	data operator * (int tim){
		data ans;
		lor(i,1,n) ans.num[i]=num[i]*tim;
		lor(i,1,n+10) ans.num[i+1]+=ans.num[i]/10,ans.num[i]%=10;
		ror(i,0,1000) if(ans.num[i]) {ans.n=i; break;}
		return ans;
	}

	data operator / (int div){
		data ans;
		int rec=0;
		ror(i,1,n){
			rec=(rec<<1)+(rec<<3)+num[i];
			while(rec>=div) ans.num[i]++,rec-=div;
		}
		ror(i,1,1000) if(ans.num[i]) {ans.n=i; break;}
		return ans;
	}
};
typedef data gjd;

const int MAX=55;

int n;
gjd n1,n2,n3,n4,c[MAX][MAX],f[MAX],g[MAX];

inline int read();
void init();
gjd qsm(int,int);

int main(){
	#ifndef ONLINE_JUDGE
	freopen("test.in","r",stdin);
	#endif

	init();

	while(n=read(),n){
		f[n].print(); printf("\n");
	}
	

	return 0;
}

inline int read(){
	char tmp=getchar(); int sum=0; bool flag=false;
	while(tmp<'0'||tmp>'9'){
		if(tmp=='-') flag=true;
		tmp=getchar();
	}
	while(tmp>='0'&&tmp<='9'){
		sum=(sum<<1)+(sum<<3)+tmp-'0';
		tmp=getchar();
	}
	return flag?-sum:sum;
}

void init(){
	c[0][0].init("1");
	lor(i,1,50) c[i][0].init("1"),c[i][i].init("1");
	lor(i,2,50) lor(j,1,i-1) c[i][j]=c[i-1][j-1]+c[i-1][j];

	lor(i,1,50){
		lor(j,1,i-1){
			gjd tmp=qsm(2,(i-j)*(i-j-1)/2)*f[j]*c[i-1][j-1];
			g[i]=g[i]+tmp;

		}
		f[i]=qsm(2,(i)*(i-1)/2)-g[i];
	}
}

gjd qsm(int base,int k){
	data ans,skip;
	ans.init(1); skip.init(base);
	while(k){
		if(k&1) ans=ans*skip;
		skip=skip*skip;
		k>>=1;
	}
	return ans;
}
上一篇:屏蔽USB插入时的弹出窗口


下一篇:Zookeeper基础命令操作