学习笔记:树与图上的计数问题

Prüfer 序列

\(n\) 个点的有标号无根树可以与一个长度为 \(n - 2\) 的 Prüfer 序列对应。

从树到 Prüfer 序列

  1. \(f\) 为空序列。
  2. 如果当前树上多于两个节点,假设当前标号最小的叶子为 \(x\),与 \(x\) 相连的节点标号为 \(y\),那么把 \(x\) 从树上删除,把 \(y\) 加入 \(f\) 末尾。
  3. 重复 2. 直到树上只有两个节点。

性质

  1. 在构造完 Prufer 序列后原树中会剩下两个节点,其中一个一定是编号最大的点 \(n\)
  2. 每个节点在序列中出现的次数是其度数减 \(1\),因此没有出现的就是叶节点。

实现

可以 \(O(n)\) 实现这个转换。

由于最后一定会留下 \(n\),不妨钦定 \(n\) 为根(把 \(n\) 拎起来)。

定义:

  • \(f_i\)\(i\) 的父节点。
  • \(d_i\)\(i\) 的儿子数量。
  • \(p_i\) 为 prufer 序列的第 \(i\) 项。

流程:

定义 \(i\) 表示当前编号最小的叶子的指针,\(cur\) 为当前序列填到哪一项。

  • \(p_{cur} = f_i\)
  • \(d_{p_{cur}}\)\(1\),表示删去 \(i\) 这个节点。
  • 如果 \(d_{p_{cur}} = 0\)\(p_{cur} < i\),那么 \(p_{cur}\) 比当前最小值还要小,直接令 \(p_{cur + 1} = f_{p_{cur}}\)
void Tree_to_Prufer() {
	for(int i = 1; i < n; ++ i) {
		cin >> f[i];
		++ d[f[i]];
	}
	for(int cur = 0, i = 1; cur < n - 2; ++ i) {
		while(d[i]) ++ i;
		p[++ cur] = f[i];
		while(cur < n - 2 && -- d[p[cur]] == 0 && p[cur] < i) {
			p[cur + 1] = f[p[cur]];
			++ cur;
		}
	}
	for(int i = 1; i <= n - 2; ++ i) cout << p[i] << ' ';
}

从 Prüfer 序列到树

  1. 找到当前不在 \(f\) 中且还未使用的最小元素 \(x\)。(等价于是叶子节点)
  2. \(x\)\(f\) 的第一个元素连边。
  3. 删除 \(f\) 的第一个元素,如果 \(f\) 非空,重复上述过程。(把叶子删掉,当前节点度数减一)
  4. 最后还剩两个点未使用,将他们连边。

代码实现类似。

void Prufer_to_Tree() {
	for(int i = 1; i <= n - 2; ++ i) {
		cin >> p[i];
		++ d[p[i]];		// 以 $n$ 为根,儿子数量
	}
	p[n - 1] = n;
	for(int cur = 1, i = 1; cur <= n - 1; ++ cur, ++ i) {
		while(d[i]) ++ i;
		f[i] = p[cur];
		while(cur < n - 1 && -- d[p[cur]] == 0 && p[cur] < i) {
			f[p[cur]] = p[cur + 1];
			++ cur;
		}
	} 
	for(int i = 1; i <= n - 1; ++ i) cout << f[i] << ' ';
}

Caylay 定理

\(n\) 个点的有标号无根树有 \(n^{n -2}\) 种。

例题

P6086 【模板】Prufer 序列

submission

P2290 [HNOI2004] 树的计数

题意:给定每个点的度数 \(d_i\),求不同的有标号无根树个数。

相当于 \(d_i - 1\)\(i\) 排进长为 \(n - 1\) 的序列的方案数。

\[\dfrac{(n - 1)!}{(d_1 - 1)!(d_2 - 1)!\cdots(d_n - 1)!} \]

由于保证了答案小于 \(10^{17}\),可以不用高精,全程用一个大于 \(10^{17}\) 的质数取模(如 \(\text{4179340454199820289}\))。

特判无解,根据 prufer 序列的性质,一定有 \(\sum d_i - 1 = n - 2\)

submission

CF1267F Foolpruf Security

题意:二分图 \(G\) 左部有 \(n\) 个节点,标号 \(1, 2, \cdots, n\),右部 \(m\) 个节点,标号 \(n + 1, n + 1, \cdots, n + m\)

满足图 \(G\) 是一棵树。

给定其 prufer 序列的两个子序列 \(a, b\),其中 \(\forall a_i \le n\)\(\forall b_i > n\),让你重构一张可能的图,或判无解。

prufer 序列最后剩下的两个点,一定一个在左边,一个在右边。

于是左边删了 \(m - 1\) 个点,则 prufer 序列中不大于 \(n\) 的元素被添加 \(m - 1\) 次。

同理,大于 \(n\) 的元素右 \(n - 1\) 个。

因此如果 \(|a| \ge m\)\(|b| \ge n\),则判无解,否则如果长度不够,随便填满。

代码与模板类似,稍加改动使得左右端点分局两侧。

void Prufer_to_Tree() {
	b[n] = n + m;
	for(int ia = 1, ib = 1, j = 1; ia <= m - 1 || ib <= n; ++ j) {
		while(d[j]) ++ j;
		int cur;
		if(j <= n) {
			cout << j << ' ' << b[ib] << '\n';
			cur = b[ib];
		}
		else {
			cout << j << ' ' << a[ia] << '\n';
			cur = a[ia];
		}
		while(1) {
			if(cur <= n) {
				if(ia <= m - 1 && -- d[a[ia]] == 0 && a[ia] < j) {
					cout << a[ia] << ' ' << b[ib] << '\n';
					++ ia;
					cur = b[ib];
				}
				else break;
				
			}
			else {
				if(ib <= n && -- d[b[ib]] == 0 && b[ib] < j) {
					cout << b[ib] << ' ' << a[ia] << '\n';
					++ ib;
					cur = a[ia];
				}
				else break;
			}
		}
		cur <= n ? ++ ia : ++ ib;
	}
}

submission

BZOJ4766 文艺计算姬

题意:完全二分图 \(K(n, m)\) 的生成树个数。

根据上题结论,将其 prufer 序列分为大于 \(n\) 和不大于 \(n\) 的两部分,长度分别为 \(n - 1\)\(m - 1\)

所以总方案为 \(m^{n - 1}n^{m - 1}\)

submission

E-Valuable Forests

题意:定义树 \(T\) 的权值 \(w(T) = \sum_{i = 1}^n\deg(i)^2\),森林 \(G\) 的权值为 \(\sum_{T\in G} w(T)\),求所有 \(n\) 元有标号森林的权值和。

定义 \(f(n)\) 表示不同 \(n\) 元森林个数。

枚举元素 \(n\) 所在树的大小,在剩下 \(n - 1\) 个元素里选 \(i - 1\) 个与 \(n\) 构成一棵树,这棵树有 \(i^{i - 2}\) 种形态。

\[f(n) = \sum_{i = 1}^{n}\begin{pmatrix}n - 1\\i - 1\end{pmatrix}i^{i - 2}f(n - i) \]

定义 \(g(n)\) 表示不同 \(n\) 元树的权值和。

枚举每个点 \(i\) 的权值 \(d\),则贡献为满足恰有 \(d - 1\) 个位置是 \(i\) 的 prufer 序列个数。

\[g(n) = \sum_{i = 1}^n\sum_{d = 1}^{n - 1}d^2\begin{pmatrix}n - 2\\d - 1\end{pmatrix}(n - 1)^{n - 2 - (d - 1)} \]

定义 \(F(n)\)\(n\) 元森林权值和。

枚举 \(n\) 所在树的大小 \(i\)\(g(i)\) 的贡献为 \(n - i\) 元森林数量,\(F(n - i)\) 贡献为 \(i^{i - 2}\)

\[F(n) = \sum_{i = 1}^n\begin{pmatrix}n - 1\\i - 1\end{pmatrix}\bigg[g(i)\cdot f(n - i) + F(n - i) \cdot i^{i - 2} \bigg] \]

submission

Matrix Tree Theorem

设无向图 \(G= (V, E), \ D = \text{diag}(d_1, d_2, \cdots, d_n)\)\(d_i\)\(i\) 的度数,\(G\) 的拉普拉斯矩阵 \(L = D - E\),则 \(G\) 的生成树个数为 \(\det(L_0)\),其中 \(L_0\)\(L\) 去掉第 \(i\) 行第 \(i\) 列(\(i\) 任取)。

\[\begin{aligned} D = \begin{pmatrix} 2 \\ & 1\\ & & 2\\ & & & 4\\ & & & & 3 \end{pmatrix} \quad\quad E = \begin{pmatrix} 0 & 0 & 0 & 1 & 1\\ 0 & 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1 & 1\\ 1 & 1 & 1 & 0 & 1\\ 1 & 0 & 1 & 1 & 0 \end{pmatrix} \end{aligned} \]

\[L = \begin{pmatrix} 2 & 0 & 0 & -1 & -1\\ 0 & 1 & 0 & -1 & 0\\ 0 & 0 & 2 & -1 & -1\\ -1 & -1 & -1 & 4 & -1\\ -1 & 0 & -1 & -1 & 3 \end{pmatrix} \]

删去第 \(4\) 行第 \(4\) 列:

\[\begin{aligned} |L_0| &= \left | \begin{matrix} 2 & 0 & 0 & -1\\ 0 & 1 & 0 & 0\\ 0 & 0 & 2 & -1\\ -1 & 0 & -1 & 3 \end{matrix} \right |\\ \\ &= 2 \left | \begin{matrix} 1 & 0 & 0\\ 0 & 2 & -1\\ 0 & -1 & 3 \end{matrix} \right | - (-1) \left | \begin{matrix} 0 & 0 & -1\\ 1 & 0 & 0\\ 0 & 2 & -1\\ \end{matrix} \right |\\ \\ &= 2 \left | \begin{matrix} 2 & -1\\ -1 & 3 \end{matrix} \right | - \left | \begin{matrix} 0 & -1\\ 2 & -1\\ \end{matrix} \right |\\ \\ &= 8 \end{aligned} \]

posted @ 2024-05-23 12:26  Lu_xZ  阅读(23)  评论(0编辑  收藏  举报