浅谈 Prüfer 序列
Prüfer 序列的定义
Prüfer 序列常用于组合计数。
Prüfer 序列可以将一个带标号的 $n$ 个节点的数用 $[1,n]$ 中的 $n-2$ 个整数表示
或者可以认为 Prüfer 序列是「完全图的生成树」与「数列」的双射。
注意,Prüfer 序列的长度为 $n-2$ !
例如,下图的带标号无根树的 Prüfer 序列为 $\{4,4,4,5\}$ 。
Prüfer 序列与无根树的相互转化
无根树的 Prüfer 序列的构造方法如下:
每次选择一个编号最小的叶子节点并删除它,然后记录它连接到的那个节点
重复 $n-2$ 次后就只剩下两个节点,算法结束。
其实这个过程就是把外面一层一层的删掉,最后剩下两个节点
我们可以用一个指针 $p$ 来寻找维护「编号最小的叶子节点」
具体地,我们可以先把无根树随便找个节点 $\mathtt{dfs}$ 后转成有根树
注:板子题里直接默认 $n$ 为转化后的根节点,并给出了每个节点的父亲。(挺良心的)
我们先找到原树「编号最小的叶子节点」,记为 $u$ ,并记它的父节点为 $v$ 。
然后我们删除 $u$ 。
- 如果 $v$ 成为了新的叶子节点,且 $v$ 编号比 $u$ 小,则直接继续删除。
- 否则,自增 $p$ 来寻找下一个编号最小的叶节点。
时间复杂度 $O(n)$ ,代码:
void t2p() // tree to prüfer
{
// f[i]为i的父节点, d[i]为i的度数, p[i]为prüfer序列
// 题目默认了转化后的根为n
for(int i=1; i<n; i++) { read(f[i]); ++d[f[i]]; }
for(int i=1,j=1; i<=n-2; i++,j++)
{
while(d[j]) ++j; p[i]=f[j];
while(i <= n-2 && !(--d[p[i]]) && p[i] < j) p[i+1]=f[p[i]],++i;
}
for(int i=1; i<=n-2; i++) ans ^= i * p[i]; // 题目要求这么输出的。
}
Prüfer 序列构造无根树的方法
- 每次我们选择一个编号最小且度数为 $1$ 的节点
- 与当前枚举到的 Prüfer 序列的点连接,然后同时减掉两个点的度数。
重复 $n-2$ 次之后就只剩下两个度数为 $1$ 的节点
然后我们把剩下的两个点连一条边就好了
时间复杂度 $O(n)$ ,代码:
void p2t() // prüfer to tree
{
// f[i]为i的父节点, d[i]为i的度数, p[i]为prüfer序列
for(int i=1; i<=n-2; i++) { read(p[i]); ++ d[p[i]]; }
p[n-1]=n; // 题目默认了转化后的根为n
for(int i=1,j=1; i<n; i++,j++)
{
while(d[j]) ++j; f[j]=p[i];
while(i < n && !(--d[p[i]]) && p[i] < j) f[p[i]] = p[i+1], ++i;
}
for(int i=1; i<n; i++) ans ^= i*f[i]; // 题目要求这么输出的。
}
Prüfer 序列的性质
定理1:Prüfer 序列与无根树一一对应。
证明:由定义与构造方法易得。$\square$
定理2:度数为 $d_i$ 的节点在 Prüfer 序列中出现 $d_i -1$ 次
证明:某个点度数为 $1$ 时,会被直接删除
否则每少掉一个相邻的节点,它就会在原序列中出现一次。
故出现次数为 $d_i-1$ 。 $\square$
定理3(Cayley公式):一个带标号的 $n$ 个节点的完全图的生成树个数有 $n^{n-2}$ 个。
或者说,$n$ 个带标号的节点,连 $n-1$ 条边后能组成多少种本质不同的树
证明:对于一个无标号的 $n$ 个点的无根树,它的 Prüfer 序列长为 $n-2$ 。
而每个位置有 $n$ 种可能性,因此可能的 Prüfer 序列共有 $n^{n-2}$ 种。
根据定理1可知,Prüfer 序列与无根树一一对应。因此原命题得证。$\square$
定理4:对于给定度数 $d_{1\sim n}$ 的一棵无根树一共有 $\dbinom{n-2}{d_1-1,d_2-1,\cdots,d_n-1}=\dfrac{(n-2)!}{\prod_{i=1}^n(d_i-1)!}$ 种情况。
证明:由定理2可知,度数为 $d_i$ 的节点会在 Prüfer 序列中出现 $d_i-1$ 次
则情况数等价于多重集 $S=\{(d_i-1) \cdot i\}$ 的排列数 多重集的排列数(多重组合数)。$\square$
参考文献:
[1] https://www.luogu.com.cn/blog/TheLostWeak/solution-p2290
[2] https://www.luogu.com.cn/blog/xht37/solution-p6086
[3] https://zh.wikipedia.org/wiki/%E6%99%AE%E5%90%95%E5%BC%97%E5%BA%8F%E5%88%97
题外话:
也写作 prufer 序列,因为好多人不知道怎么打出这个 ü ,或者打出来很麻烦。