注意:

  • 高精度!

实现树的Prufer编码

Prufer编码是用另外一种形式来描述一棵树,这棵树是无根树,它可以和无根树之间形成一一对应关系。已知树,如何求Prufer编码?
首先选这棵树叶子中编号最小的点,将这个点删除,并且把它的邻接点加入一个数组中,例如第一个删除的节点为1,并且把5加入数组中。删除节点后形成一棵新的树,再在新树中删除最小的节点,并且把邻接点加入数组中,这样重复以上步骤,直到树中最后剩余两个点的时候终止操作。这时候数组中的便是Prufer编码。

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

将Prufer编码还原为一棵树

假如Prufer编码为(a1,a2,a3,an2)在上述数组中,在数组最后加入n这个值,这样便形成了数组中包含n1个节点,例如上述为(5,5,4,4,4,6,8)
然后取不在数组中的最小值为b1,则b1a1是邻接点,在数组中删除a1,再在剩下的数中选取不为b1,且不在数组中的最小值为b2,则b2a2是邻接点,这样依次循环下去直到结束,这样便形成了一棵树。

Prufer编码的性质

Cayley 定理:不同的n节点标号树的数量为nn2
任意一棵n节点的树都可唯一的用长度为n2的Prufer编码表示;度数为m的节点的序号在Prufer编码中出现的次数为m1

例题:[HNOI2008]明明的烦恼

题目描述

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

输入格式

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

输出格式

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

输入

1
2
3
4
3
1
-1
-1

输出

1
2

说明/提示

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

题目分析

该题需要将树转化为prufer编码:
n为树的节点数,di为各节点的度数,m为无限制度数的节点数。
则有度数的点出现次数为:tot=i=1n(di1),因为度数为di的点出现了di1
所以要求在n2大小的数组中插入tot个序号,共有Cn2tot种插法;
tot个序号排列中,插第一个节点的方法有Ctotd11种插法;
插第二个节点的方法有Ctotd21种插法;
…………
另外还有m个节点无度数限制,所以它们可任意排列在剩余的n2tot的空间中,排列方法总数为mn2tot
根据乘法原理:

ans=Cn2totCtotd11Ctot(d11)d21Cdn1dn1mn2tot=(n2)!(n2tot)!tot!tot!(d11)!(totd1+1)!(dn1)!(dn1)!0!mn2tot=(n2)!mn2tot(n2tot)!(d11)!(d21)!(dn1)!

然后就要高精度了……但高精度除法太麻烦了,显而易见的排列组合一定是整数,所以可以进行质因数分解,再做一下相加减。
关于 n!质因数分解有两种方法,第一种暴力分解,这里着重讲第二种。
p为素数,则n!可分解为一个数px,其中x=np+np2++nptpt<n
所以n!=p1x1p2x2p3x3pmxm(pm<n)
代码详见Code

模板题

讲义下载