栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

题解 有标号无向连通图计数

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

题解 有标号无向连通图计数

acwing link。

题意

求 N N N 个节点的无向连通图有多少个,节点有标号,编号为 1 ∼ N 1sim N 1∼N。例如下列图示,三个节点的无向连通图共 4 4 4 个。

题解

设 d p i dp_i dpi​ 表示 i i i 个节点的无向连通图的个数。求连通图个数不好求,考虑求不连通图个数。则 d p i = a l l i − n o t C o n n e c t e d i dp_i=all_i-notConnected_i dpi​=alli​−notConnectedi​。

i i i 个节点的无向图有 C i 2 C_i^2 Ci2​ 条边,每条边可连可不连,所以 a l l i = 2 C i 2 all_i=2^{C_i^2} alli​=2Ci2​。

现在来考虑 n o t C o n n e c t e d i notConnected_i notConnectedi​。首先肯定要枚举什么东西。枚举每条边连不连?那就是 a l l all all 了。枚举断开的两个点?那很难判断连通性。有个好方法:枚举节点 1 1 1 所在连通块大小 j j j。这样既保证了每种情况都能考虑到,也不会重复计算。

首先枚举 j ∈ [ 1 , i ) jin[1,i) j∈[1,i),对于每个 j j j,节点 1 1 1 所在连通块的节点选择数有 C i − 1 j − 1 C_{i-1}^{j-1} Ci−1j−1​ 种(从 1 1 1 以外 i − 1 i-1 i−1 个节点中取 j − 1 j-1 j−1 个与 1 1 1 在同一个连通块里),而具体边怎么连呢?已经有了答案: d p j dp_j dpj​。剩下的 i − j i-j i−j 个节点怎么连都行了,答案为 a l l i − j all_{i-j} alli−j​。所以能够得出递推式:

d p i = 2 C i 2 − ∑ j = 1 i − 1 C i − 1 j − 1 × d p j × 2 C i − j 2 dp_i=2^{C_i^2}-sumlimits_{j=1}^{i-1}C_{i-1}^{j-1}times dp_jtimes2^{C_{i-j}^2} dpi​=2Ci2​−j=1∑i−1​Ci−1j−1​×dpj​×2Ci−j2​

#include 
using namespace std;

const int N = 9;
long long C[N][N], bit[N*N], dp[N];

void solve(){
	bit[0] = 1; for(int i = 1; i < N*N; ++ i) bit[i] = bit[i-1] * 2;
	for(int i = 0; i < N; ++ i){
		C[i][0] = C[i][i] = 1;
		for(int j = 1; j < i; ++ j) C[i][j] = C[i-1][j-1] + C[i-1][j];
		dp[i] += bit[C[i][2]];
		for(int j = 1; j < i; ++ j)
			dp[i] -= dp[j] * C[i-1][j-1] * bit[C[i-j][2]];
	}
}

但是很快能发现问题:当 n n n 取到 10 10 10 时,就已经爆 long long 了,所以要高精度。

高精度代码

把进位、退位集中在一个函数解决,可以大幅降低写代码的难度。

#include 
using namespace std;

const int N = 52;
struct BigInteger{
	int a[1010], len;
	BigInteger(){ memset(a, 0, sizeof(a)); len = 0; }
	void fix(){
		for(int i = 0; i < len; ++ i){
			if(a[i] < 0) --a[i+1], a[i] += 10;
			a[i+1] += a[i]/10, a[i] %= 10;
		}
		while(a[len] >= 10) a[len+1] += a[len]/10, a[len] %= 10, ++ len;
		while(a[len]) ++ len;
		while(!a[len-1]) -- len;
	}
	void print(){ for(int i = len-1; i >= 0; -- i) putchar(a[i]+'0'); puts(""); }
	void set1(){ a[0] = 1; len = 1; }
	void mul2(){ for(int i = 0; i < len; ++ i) a[i] *= 2; fix(); }
	BigInteger operator + (const BigInteger &y) const {
		BigInteger ans; ans.len = max(len, y.len);
		for(int i = 0; i < ans.len; ++ i) ans.a[i] = a[i] + y.a[i];
		ans.fix(); return ans;
	}
	BigInteger operator - (const BigInteger &y) const {
		BigInteger ans; ans.len = max(len, y.len); 
		for(int i = 0; i < ans.len; ++ i) ans.a[i] = a[i] - y.a[i];
		ans.fix(); return ans;
	}
	BigInteger operator * (const BigInteger &y) const {
		BigInteger ans; ans.len = len + y.len - 1;
		for(int i = 0; i < len; ++ i) for(int j = 0; j < y.len; ++ j) ans.a[i+j] += a[i] * y.a[j];
		ans.fix(); return ans;
	}
} C[N][N], bit[N*N], dp[N];

int main(){
	bit[0].set1(); for(int i = 1; i < N*N; ++ i){ bit[i] = bit[i-1]; bit[i].mul2(); }
	for(int i = 0; i < N; ++ i){
		C[i][0].set1(); C[i][i].set1();
		for(int j = 1; j < i; ++ j) C[i][j] = C[i-1][j-1] + C[i-1][j];
		dp[i] = dp[i] + bit[i*(i-1)/2];
		for(int j = 1; j < i; ++ j)
			dp[i] = dp[i] - dp[j] * C[i-1][j-1] * bit[(i-j)*(i-j-1)/2];
	}
	
 	int n; while(scanf("%d", &n) && n) dp[n].print();
	return 0;
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/942699.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号