Prüfer 序列

创建时间:2025-08-10


Prüfer 序列的概念与构造 Prüfer 序列

有时树或序列的问题很难直接处理,想到将一棵树转化为一个序列或将序列转化为树。转化的方式有很多种,最常见的有 dfs 序、欧拉序,Prüfer 序列也是其中的一种。与前两者不同的是,前两种方式的序列与树之间的映射不是唯一的,而 Prüfer 序列恰好满足一棵树只有一个 Prüfer 序列,一个 Prüfer 序列只对应一棵树。

\(fa_i\) 为树中 \(i\) 的父节点编号,\(deg_i\) 为树中 \(i\) 的度数,\(prufer_{1 \sim n-2}\) 为 Prüfer 序列。

得到 Prüfer 序列需要不断进行以下操作:

  1. 找到度数为 \(1\) 的节点,即叶子节点中编号最小的节点,记为 \(leaf\)
  2. 对于第 \(i\) 次操作得到的 \(leaf\),令 \(prufer_i=fa_{leaf}\)
  3. 删掉 \(leaf\),即 \(deg_{fa_{leaf}}:=deg_{fa_{leaf}}-1\)

显然,在 \(n-2\) 次操作后剩下了联通的两个节点,其中一定有一个是树的根。

这么做得到的序列是唯一的。

按逻辑写出代码:

void get_prufer() {
	int cur, leaf;
	for (int i = 1; i <= n; i++)
		if (deg[i] == 1) {
			cur = leaf = i;
			break;
		}
	for (int i = 1; i <= n - 2; i++) {
		prufer[i] = fa[leaf];
		deg[fa[leaf]]--;
		if (deg[fa[leaf]] == 1 && fa[leaf] < cur) {
			leaf = fa[leaf];
			continue;
		}
		cur++;
		while (deg[cur] != 1)
			cur++;
		leaf = cur;
	}
}

因为 \(cur\) 是单调不减的,\(leaf\) 也最多不超过 \(n\) 次,故该做法是线性的。

Prüfer 序列的性质及还原树结构

Prüfer 序列有以下几个常用性质:

1:构造 Prüfer 序列剩余的两个节点中必然有一个编号为 \(n\)

假设剩余节点中没有 \(n\),则 \(n\) 一定在先前的操作中被删了。而当且仅当 \(n\) 的度数为 \(1\) 且不存在其他度数为 \(1\) 的节点时 \(n\) 会被删掉(因为 \(n\) 的编号比其他任何节点都大)。感性的理解会发现,在任何时刻,一颗树中总是有至少两个度数为 \(1\) 的节点。这与之前的推导矛盾,故假设不成立,\(n\) 自然会是剩余节点中的一个。

2:\(i\) 在 Prüfer 序列中的出现次数恰好为 \(deg_i-1\)

因为当且仅当每当孩子节点被操作时,\(i\) 被添加到 Prüfer 序列中,所以 \(i\) 在 Prüfer 序列中出现次数就等于 \(i\) 的被操作过的孩子个数。当 \(i\) 为根时,\(i\) 一定是剩余节点中的一个,而另一个一定是 \(i\) 的孩子,所有与 \(i\) 相连的点都是 \(i\) 的孩子,故 \(i\) 的被操作过的孩子个数等于 \(deg_i-1\);当 \(i\) 不为根时,与 \(i\) 相连的节点中除了 \(i\) 的父节点都是 \(i\) 的孩子,且 \(i\) 的所有孩子都被操作过,所以 \(i\) 的被操作过的孩子个数也是 \(deg_i-1\)。综上知 \(i\) 在 Prüfer 序列中出次数一定为 \(deg_i-1\)

有了这两个结论便能由 Prüfer 序列倒推得到原树:

void get_tree() {
	fill(deg + 1, deg + n + 1, 1); 
	for (int i = 1; i <= n - 2; i++) 
		deg[prufer[i]]++;					// 性质1 
	int cur, leaf;
	for (int i = 1; i <= n; i++)
		if (deg[i] == 1) {
			cur = leaf = i;
			break;
		}
	for (int i = 1; i <= n - 2; i++) {
		fa[leaf] = prufer[i];
		deg[fa[leaf]]--;
		if (deg[fa[leaf]] == 1 && fa[leaf] < cur) {
			leaf = fa[leaf];
			continue;
		}
		cur++;
		while (deg[cur] != 1)
			cur++;
		leaf = cur;
	}
	fa[leaf] = n;							// 性质2(fa[n]=leaf等价)
}

Cayley 公式和广义 Cayley 公式

Cayley 公式:对于由 \(n\) 个节点构成的无根树的结构有 \(n^{n-2}\)

因为任意一个 Prüfer 序列显然都是可以且只对应一颗树的,而 Prüfer 序列的第 \(i\) 项一共有 \(n\) 种可能,一共 \(n-2\) 项。故总可能数为 \(n^{n-2}\)

广义 Cayley 公式:对于 \(n\) 个节点,其中有 \(t\) 个特殊点,构成 \(t\) 棵树使任意两个特殊点不在同一颗树中,有 \(t n^{n-t-1}\) 种方案(\(t<n\))。

不妨改变节点编号使 \(t\) 个关键点编号分别为 \(n,n-1,n-2,\cdots,n-t+2,n-t+1\)。考虑将每个关键点视为其所在的树的根,虚拟节点 \(n+1\),将所有关键节点作为 \(n+1\) 的孩子,这样便构成了一棵包含 \(n+1\) 个节点的树。很显然的是,这棵新树的 Prüfer 序列有 \(n-1\) 项的最后 \(t-1\) 项均为 \(n+1\)。故新树的 Prüfer 有 \(n^{n-t-1}\) 种可能。而在建 Prüfer 序列后会剩下两个节点,这两个节点中一个一定为 \(n+1\),一个一定为某个关键点,这个关键点有 \(t\) 种可能,想由新树映射会原森林就必须直到剩下的节点。综上得总方案数 \(tn^{n-t-1}\)

Prüfer 序列的其他常用结论与例题

1:\(n\) 个节点的有根树的结构有 \(n^{n-1}\) 种。

这是显然的,因为有根树个数等价于任意一颗无根树中选一个根的的方案数,无根树个数为 \(n^{n-2}\),选根有 \(n\) 中选择,故总数为 \(n^{n-2}\cdot n=n^{n-1}\)

2:\(\sum_{i=1}^n (deg_i-1)=n-2\)

由性质2知 \(i\) 在 Prüfer 序列中出现了 \(deg_i-1\) 次,而 Prüfer 序列的长度为 \(n-2\),故上式显然成立。

3:若已知 \(deg\) 数组,则满足条件的无根树个数为

\[\frac {(n-2)!} {(deg_1-1)!(deg_2-1)!(deg_3-1)\cdots(deg_n-1)!} \]

因为无根树个数等价于 Prüfer 序列的个数,又由性质2知个数即为 \(deg_1-1\)\(1\)\(deg_2-1\)\(2\)\(deg_3-3\)\(\cdots\)\(deg_n-1\)\(n\) 的排列总数,即 \(\frac {((deg_1-1)+(deg_2-1)+(deg_3-1)+\cdots+(deg_n-1))!} {(deg_1-1)!(deg_2-1)!(deg_3-1)\cdots(deg_n-1)!}=\frac {n-2}{\Pi_{i=1}^n(deg_i-1)!}\)

应用:
P6086 【模板】Prüfer 序列(板子题)
P2624 [HNOI2008] 明明的烦恼(Prüfer 序列+组合+高精度)
CF156D Clues(Prüfer 序列+多元二项式定理)
P5454 [THUPC 2018] 城市地铁规划(Prüfer 序列+完全背包)
CF1109D Sasha and Interesting Fact from Graph Theory(广义 Cayley 公式)

posted @ 2026-06-02 16:18  xubaichuan  阅读(17)  评论(0)    收藏  举报