生成树
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了生成树相关的知识,希望对你有一定的参考价值。
[BZOJ 3534] 重建
题意
给定一张 n (1 < n <= 50) 个点 m 条边的无向图, 每条边有一定出现的概率, 问出现的边恰好形成一个生成树的概率.
实现
注意到对于出现概率为 1 的边, 分母可能为 0 . 我们对 d = 1 变换为 d = 1-EPS 就好了.
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cctype> 5 #include <cmath> 6 #include <algorithm> 7 using namespace std; 8 #define F(i, a, b) for (register int i = (a); i <= (b); i++) 9 #define db double 10 11 const db EPS = 1e-9; 12 inline int sign(db x) { return fabs(x)<EPS ? 0 : x<0 ? -1 : 1; } 13 inline int cmp(db x, db y) { return sign(x-y); } 14 15 const int N = 55; 16 17 int n; db f[N][N], Div = 1; 18 inline void Init(int x, int y, db w) { 19 f[x][x] += w, f[y][y] += w, f[x][y] -= w, f[y][x] -= w; 20 } 21 db Det(void) { 22 n--; 23 db Ret = 1; 24 F(i, 1, n) { 25 int id = i; 26 while (sign(f[id][i]) == 0) id++; 27 if (id > n) return 0; 28 if (id != i) { 29 F(k, 1, n) swap(f[i][k], f[id][k]); 30 Ret = -Ret; 31 } 32 Ret *= f[i][i]; 33 F(j, i+1, n) if (sign(f[j][i]) != 0) { 34 db t = f[j][i] / f[i][i]; 35 F(k, i, n) f[j][k] -= t * f[i][k]; 36 } 37 } 38 return Ret; 39 } 40 41 int main(void) { 42 #ifndef ONLINE_JUDGE 43 freopen("bzoj3534.in", "r", stdin); 44 #endif 45 46 scanf("%d", &n); 47 F(i, 1, n) F(j, 1, n) { 48 db p; scanf("%lf", &p); 49 if (i < j) { 50 if (cmp(p, 1) == 0) p -= EPS; 51 Div *= 1-p; 52 Init(i, j, p/(1-p)); 53 } 54 } 55 printf("%0.8lf\n", Div * Det()); 56 57 return 0; 58 }
[BZOJ 1016] 最小生成树计数
题意
给定一张 n (n <= 16) 个点的图, 求有多少棵最小生成树.
分析
Kruskal 算法告诉我们, 每种边使用的数量相同, 它们构成的连通性相同.
在 Kruskal 的同时, 该权值作用之前的连通块当做若干个点, 它们在该权值作用之后形成若干个连通块, 对每个连通块进行生成树计数.
实现
使用 vector 进行分类重标号.
Mashmokh‘s Designed Problem 01生成树
题意
给一张 n (1 <= n <= 100000) 个点 m (1 <= m <= 100000) 条边的无向连通图, 每条边的权值可能为 0 或 1 , 问是否存在一种生成树权值为 x ?
分析
考虑构建最小生成树, 最大生成树, 最小生成树的权值为 Min, 最大生成树的权值为 Max , 显然能构造出来的权值只可能在 [Min, Max] 之间.
最小生成树一定可以通过若干次消圈得到最大生成树, 而边权又是 0 或 1, 所以变化是连续的, 所以 [Min, Max] 之间的所有权值都可以构造出来.
综上, 存在一种生成树权值为 x 当且仅当 Min <= x 且 x <= Max .
[Hdu 5304] 基环树计数
题意
给定一张 n 个点 m 条边的无向图, 问其有多少个基环生成树.
n <= 16 , 无重边, 无自环.
分析
利用状压DP 求每种环的状态的个数.
枚举环, 缩点, 利用 Matrix-Tree定理 进行计数.
实现
f[s][i] 表示从 s 状态中的最小值出发, 终点在 i 的方案数.
边界 f[2 ^ i][i] = 1 .
统计答案的时候, 对于 f[s][i] , 若 s 中的个数大于 2 , 且 i 能到达 s 状态中的最小值, 则 cnt[s] += f[s][i] .
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cctype> 5 #include <algorithm> 6 using namespace std; 7 #define F(i, a, b) for (register int i = (a); i <= (b); i++) 8 #define D(i, a, b) for (register int i = (a); i >= (b); i--) 9 inline int rd(void) { 10 int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == ‘-‘) f = -1; 11 int x = 0; for (; isdigit(c); c = getchar()) x = x*10+c-‘0‘; return x*f; 12 } 13 14 const int N = 20; 15 const int S = 70000; 16 const int MOD = 998244353; 17 18 inline void Add(int &x, int y) { x = (x + y) % MOD; } 19 inline int Pow(int x, int y) { 20 int Mul = 1; 21 for (; y > 0; y >>= 1, x = 1LL * x * x % MOD) 22 if (y & 1) Mul = 1LL * Mul * x % MOD; 23 return Mul; 24 } 25 inline int Inv(int x) { return Pow(x, MOD-2); } 26 27 int n, m; bool G[N][N]; 28 int Full, cnt[S], f[S][N]; 29 int Lab[N], tot, Mat[N][N], ans; 30 31 void Build(int s) { 32 tot = 1; F(i, 1, n) Lab[i] = (s >> i-1 & 1 ? 1 : ++tot); 33 memset(Mat, 0, sizeof Mat); 34 F(i, 1, n) F(j, i+1, n) 35 if (G[i][j] && Lab[i] != Lab[j]) { 36 Mat[Lab[i]][Lab[i]]++, Mat[Lab[j]][Lab[j]]++; 37 Mat[Lab[i]][Lab[j]]--, Mat[Lab[j]][Lab[i]]--; 38 } 39 } 40 int Gauss(void) { 41 tot--; 42 int Mul = 1; 43 F(i, 1, tot) { 44 if (!Mat[i][i]) { 45 F(j, i+1, tot) if (Mat[j][i] != 0) { 46 F(k, 1, tot) swap(Mat[i][k], Mat[j][k]); 47 Mul = -Mul; 48 break; 49 } 50 } 51 if (!Mat[i][i]) return 0; 52 Mul = 1LL * Mul * Mat[i][i] % MOD; 53 int I = Inv(Mat[i][i]); 54 F(j, i+1, tot) if (Mat[j][i] != 0) { 55 int t = 1LL * I * Mat[j][i] % MOD; 56 F(k, i, tot) 57 Add(Mat[j][k], -1LL * Mat[i][k] * t % MOD); 58 } 59 } 60 return Mul; 61 } 62 63 int main(void) { 64 #ifndef ONLINE_JUDGE 65 freopen("hdu5304.in", "r", stdin); 66 #endif 67 68 int inv2 = Inv(2); 69 while (~scanf("%d %d", &n, &m)) { 70 memset(G, false, sizeof G); 71 F(i, 1, m) { 72 int x = rd(), y = rd(); 73 G[x][y] = G[y][x] = true; 74 } 75 76 Full = (1 << n) - 1, memset(cnt, 0, sizeof cnt), memset(f, 0, sizeof f); 77 F(i, 1, n) f[1 << i-1][i] = 1; 78 F(s, 1, Full) { 79 int Min = 0; D(i, n, 1) if (s >> i-1 & 1) Min = i; 80 F(i, Min, n) if ((s >> i-1 & 1) && f[s][i] != 0) { 81 F(j, Min+1, n) if (!(s >> j-1 & 1) && G[i][j]) 82 Add(f[s | 1 << j-1][j], f[s][i]); 83 } 84 } 85 F(s, 1, Full) { 86 int tot = 0; for (int x = s; x > 0 && tot <= 2; x ^= (x & -x), tot++); if (tot <= 2) continue; 87 int Min = 0; D(i, n, 1) if (s >> i-1 & 1) Min = i; 88 F(i, Min+1, n) if ((s >> i-1 & 1) && G[i][Min]) 89 Add(cnt[s], f[s][i]); 90 cnt[s] = 1LL * cnt[s] * inv2 % MOD; 91 } 92 93 ans = 0; 94 F(s, 1, Full) if (cnt[s] != 0) { 95 Build(s); 96 Add(ans, 1LL * cnt[s] * Gauss() % MOD); 97 } 98 printf("%d\n", (ans + MOD) % MOD); 99 } 100 101 return 0; 102 }
以上是关于生成树的主要内容,如果未能解决你的问题,请参考以下文章