树的prufer编码

lolifamily

注意:

  • 高精度!

实现树的 Prufer 编码

Prufer 编码是用另外一种形式来描述一棵树,这棵树是无根树,它可以和无根树之间形成一一对应关系。

已知树,如何求 Prufer 编码?

首先选这棵树叶子中编号最小的点,将这个点删除,并且把它的邻接点加入一个数组中,例如第一个删除的节点为1,并且把5加入数组中。

删除节点后形成一棵新的树,再在新树中删除最小的节点,并且把邻接点加入数组中,这样重复以上步骤,直到树中最后剩余两个点的时候终止操作。

这时候数组中的便是 Prufer 编码。

1.png

例如上图是一棵无根树,这棵树的 Prufer 编码为(5,5,4,4,4,6)

将 Prufer 编码还原为一棵树

假如 Prufer 编码为(𝑎1,𝑎2,𝑎3,𝑎𝑛2)在上述数组中,在数组最后加入𝑛这个值,这样便形成了数组中包含𝑛1个节点,例如上述为(5,5,4,4,4,6,8)

然后取不在数组中的最小值为𝑏1,则𝑏1𝑎1是邻接点,在数组中删除𝑎1,再在剩下的数中选取不为𝑏1,且不在数组中的最小值为𝑏2,则𝑏2𝑎2是邻接点,这样依次循环下去直到结束,这样便形成了一棵树。

Prufer 编码的性质

Cayley 定理:不同的𝑛节点标号树的数量为𝑛𝑛2

任意一棵𝑛节点的树都可唯一的用长度为𝑛2的 Prufer 编码表示。

度数为𝑚的节点的序号在 Prufer 编码中出现的次数为𝑚1

例题:[HNOI2008] 明明的烦恼

题目描述

自从明明学了树的结构,就对奇怪的树产生了兴趣…… 给出标号为1𝑁的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树?

输入格式

第一行为𝑁(0<𝑁1000),接下来𝑁行,第𝑖+1行给出第𝑖个节点的度数𝐷𝑖,如果对度数不要求,则输入 -1

输出格式

一个整数,表示不同的满足要求的树的个数,无解输出 0

输入

3
1
-1
-1

输出

2

说明 / 提示

两棵树分别为 1-2-3; 1-3-2

题目分析

该题需要将树转化为 prufer 编码:

𝑛为树的节点数,𝑑𝑖为各节点的度数,𝑚为无限制度数的节点数。

则有度数的点出现次数为:

𝑡𝑜𝑡=𝑛𝑖=1(𝑑𝑖1)

因为度数为𝑑𝑖的点出现了𝑑𝑖1次。

所以要求在𝑛2大小的数组中插入𝑡𝑜𝑡个序号,共有𝐶𝑡𝑜𝑡𝑛2种插法。

𝑡𝑜𝑡个序号排列中,插第一个节点的方法有𝐶𝑑11𝑡𝑜𝑡种插法。

插第二个节点的方法有𝐶𝑑21𝑡𝑜𝑡种插法。

……

另外还有𝑚个节点无度数限制,所以它们可任意排列在剩余的𝑛2𝑡𝑜𝑡的空间中,排列方法总数为𝑚𝑛2𝑡𝑜𝑡

根据乘法原理:

𝑎𝑛𝑠=𝐶𝑡𝑜𝑡𝑛2𝐶𝑑11𝑡𝑜𝑡𝐶𝑑21𝑡𝑜𝑡(𝑑11)𝐶𝑑𝑛1𝑑𝑛1𝑚𝑛2𝑡𝑜𝑡=(𝑛2)!(𝑛2𝑡𝑜𝑡)!𝑡𝑜𝑡!𝑡𝑜𝑡!(𝑑11)!(𝑡𝑜𝑡𝑑1+1)!(𝑑𝑛1)!(𝑑𝑛1)!0!𝑚𝑛2𝑡𝑜𝑡=(𝑛2)!𝑚𝑛2𝑡𝑜𝑡(𝑛2𝑡𝑜𝑡)!(𝑑11)!(𝑑21)!(𝑑𝑛1)!

然后就要高精度了…… 但高精度除法太麻烦了,显而易见的排列组合一定是整数,所以可以进行质因数分解,再做一下相加减。

关于𝑛!质因数分解有两种方法,第一种暴力分解,这里着重讲第二种。

𝑝为素数,则𝑛!可分解为一个数×𝑝𝑥,其中

𝑥=𝑛𝑝+𝑛𝑝2++𝑛𝑝𝑡

𝑝𝑡<𝑛

𝑛!=𝑝𝑥11𝑝𝑥22𝑝𝑥33𝑝𝑥𝑚𝑚(𝑝𝑚<𝑛)

代码详见 Code

模板题