Unverified Commit 05621b23 authored by partychicken's avatar partychicken Committed by GitHub
Browse files

Update prufer.md

fix typo
parent e2acc69a
Loading
Loading
Loading
Loading
+19 −19
Original line number Diff line number Diff line
本文翻译自 [e-maxx Prufer Code](https://github.com/e-maxx-eng/e-maxx-eng/blob/master/src/graph/pruefer_code.md)。另外解释一下,原文的结点是从 0 开始标号的,本文我按照大多数人的习惯改成了从 1 标号。
本文翻译自 [e-maxx Prufer Code](https://github.com/e-maxx-eng/e-maxx-eng/blob/master/src/graph/pruefer_code.md)。另外解释一下,原文的结点是从 $0$ 开始标号的,本文我按照大多数人的习惯改成了从 $1$ 标号。

这篇文章介绍 $Prufer$ 序列 (Prufer code),这是一种将带标号的树用一个唯一的整数序列表示的方法。
这篇文章介绍 Prufer 序列 (Prufer code),这是一种将带标号的树用一个唯一的整数序列表示的方法。

使用 $Prufer$ 序列可以证明凯莱定理 (Cayley's formula)。并且我们也会讲解如何计算在一个图中加边使图连通的方案数。
使用 Prufer 序列可以证明凯莱定理 (Cayley's formula)。并且我们也会讲解如何计算在一个图中加边使图连通的方案数。

**注意**:我们不考虑含有 1 个结点的树。
**注意**:我们不考虑含有 $1$ 个结点的树。

## Prufer 序列

$Prufer$ 序列可以将一个带标号 n 个结点的树用 $[1,n]$ 中的 $n-2$ 个整数表示。你也可以把它理解为完全图的生成树与数列之间的双射。
Prufer 序列可以将一个带标号 $n$ 个结点的树用 $[1,n]$ 中的 $n-2$ 个整数表示。你也可以把它理解为完全图的生成树与数列之间的双射。

显然你不会想不开拿这玩意儿去维护树结构。这玩意儿常用组合计数问题上。

@@ -16,7 +16,7 @@ Heinz Prufer 于 1918 年发明这个序列来证明凯莱定理。

### 对树建立 Prufer 序列

$Prufer$ 是这样建立的:每次选择一个编号最小的叶结点并删掉它,然后在序列中记录下它连接到的那个结点。重复 $n-2$ 次后就只剩下两个结点,算法结束。
Prufer 是这样建立的:每次选择一个编号最小的叶结点并删掉它,然后在序列中记录下它连接到的那个结点。重复 $n-2$ 次后就只剩下两个结点,算法结束。

显然使用堆可以做到 $O(n\log_2n)$ 的复杂度

@@ -111,14 +111,14 @@ vector<int> pruefer_code() {

### Prufer 序列的性质

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

### 用 Prufer 序列重建树

重建树的方法是类似的。根据 $Prufer$ 序列的性质,我们可以得到原树上每个点的度数。然后你也可以得到度数最小的叶结点编号,而这个结点一定与 $Prufer$ 序列的第一个数连接。然后我们同时删掉这两个结点的度数。
重建树的方法是类似的。根据 Prufer 序列的性质,我们可以得到原树上每个点的度数。然后你也可以得到度数最小的叶结点编号,而这个结点一定与 Prufer 序列的第一个数连接。然后我们同时删掉这两个结点的度数。

讲到这里也许你已经知道该怎么做了。每次我们选择一个度数为 1 的最小的结点编号,与当前枚举到的 $Prufer$ 序列的点连接,然后同时减掉两个点的度。到最后我们剩下两个度数为 1 的点,其中一个是结点 $n$。就把它们建立连接。使用堆维护这个过程,在减度数的过程中如果发现度数减到 1 就把这个结点添加到堆中,这样做的复杂度是 $O(n\log_2n)$ 的。
讲到这里也许你已经知道该怎么做了。每次我们选择一个度数为 $1$ 的最小的结点编号,与当前枚举到的 Prufer 序列的点连接,然后同时减掉两个点的度。到最后我们剩下两个度数为 $1$ 的点,其中一个是结点 $n$。就把它们建立连接。使用堆维护这个过程,在减度数的过程中如果发现度数减到 $1$ 就把这个结点添加到堆中,这样做的复杂度是 $O(n\log_2n)$ 的。

```cpp
// 原文摘代码
@@ -146,7 +146,7 @@ vector<pair<int, int>> pruefer_decode(vector<int> const& code) {

### 线性时间重建树

同线性构造 $Prufer$ 序列的方法。在删度数的时侯会产生新的叶结点,于是判断这个叶结点与指针 $p$ 的大小关系,如果更小就优先考虑它(原文讲得也很略所以我也不细讲啦)
同线性构造 Prufer 序列的方法。在删度数的时侯会产生新的叶结点,于是判断这个叶结点与指针 $p$ 的大小关系,如果更小就优先考虑它(原文讲得也很略所以我也不细讲啦)

```cpp
// 原文摘代码
@@ -175,31 +175,31 @@ vector<pair<int, int>> pruefer_decode(vector<int> const& code) {
}
```

通过这些过程其实可以理解,$Prufer$ 序列与带标号无根树建立了双射关系。
通过这些过程其实可以理解,Prufer 序列与带标号无根树建立了双射关系。

## Cayley 定理

凯莱定理描述如下:所有群 G 同构于在 G 上的对称群的子群。
凯莱定理描述如下:所有群 $G$ 同构于在 $G$ 上的对称群的子群。

是不是一脸懵逼不知所措

还有另一种表述:完全图 $K_n$ 有 $n^{n-2}$ 棵生成树。

怎么证明?方法很多,但是用 $Prufer$ 序列证是很简单的。任意一个长度为 $n-2$ 的值域 $[1,n]$ 的整数序列都可以通过 $Prufer$ 序列双射对应一个生成树,于是方案数就是 $n^{n-2}$。
怎么证明?方法很多,但是用 Prufer 序列证是很简单的。任意一个长度为 $n-2$ 的值域 $[1,n]$ 的整数序列都可以通过 Prufer 序列双射对应一个生成树,于是方案数就是 $n^{n-2}$。

## 图连通方案数

$Prufer$ 序列可能比你想得还强大。它能创造比凯莱定理更通用的公式。比如以下问题:
Prufer 序列可能比你想得还强大。它能创造比凯莱定理更通用的公式。比如以下问题:

> 一个 n 个点 m 条边的带标号无向图有 k 个连通块。我们希望添加 k-1 条边使得整个图连通。求方案数。
> 一个 $n$ 个点 $m$ 条边的带标号无向图有 $k$ 个连通块。我们希望添加 $k-1$ 条边使得整个图连通。求方案数。

设 $s_i$ 表示每个连通块的数量。我们对 $k$ 个连通块构造 $Prufer$ 序列,然后你发现这并不是普通的 $Prufer$ 序列。因为每个连通块的连接方法很多。不能直接淦就设啊。于是设 $d_i$ 为第 $i$ 个连通块的度数。由于度数之和是边数的两倍,于是 $\sum_{i=1}^kd_i=2k-2$。则对于给定的 $d$ 序列构造 $Prufer$ 序列的方案数是
设 $s_i$ 表示每个连通块的数量。我们对 $k$ 个连通块构造 Prufer 序列,然后你发现这并不是普通的 Prufer 序列。因为每个连通块的连接方法很多。不能直接淦就设啊。于是设 $d_i$ 为第 $i$ 个连通块的度数。由于度数之和是边数的两倍,于是 $\sum_{i=1}^kd_i=2k-2$。则对于给定的 $d$ 序列构造 Prufer 序列的方案数是

$$
\binom{k-2}{d_1-1,d_2-1,\cdots,d_k-1}=\frac{(k-2)!}{(d_1-1)!(d_2-1)!\cdots(d_k-1)}
\binom{k-2}{d_1-1,d_2-1,\cdots,d_k-1}=\frac{(k-2)!}{(d_1-1)!(d_2-1)!\cdots(d_k-1)!}
$$

对于第 i 个连通块,它的连接方式有 ${s_i}^{d_i}$ 种,因此对于给定 $d$ 序列使图连通的方案数是
对于第 $i$ 个连通块,它的连接方式有 ${s_i}^{d_i}$ 种,因此对于给定 $d$ 序列使图连通的方案数是

$$
\binom{k-2}{d_1-1,d_2-1,\cdots,d_k-1}\cdot \prod_{i=1}^k{s_i}^{d_i}