版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/m0_38081836/article/details/83754528
题意:有一个n个点的无根树,已经知道了它每个点的度数(也有可能没有限制),询问有多少种形式的无根树满足上述条件。
前置技能:
prufer序列
定义
任何一个无根树都可以由一个
序列来唯一表示,其中度为
的节点在序列中出现
次。
构造
序列:
首先选择节点序号最小的叶子节点,然后将与它相连的节点加入序列,然后把它自己删掉,直到树剩下只有两个节点为止。
如上图所以我们求他的prufer序列:
- 选择最小的叶子节点 然后将与它相连的点加入序列,得到
- 然后删除 ,重复第一步得到
- 直到最后剩下
,得到prufer序列
还原
仍为上面的树,Prufer序列为{3,5,1,3},开始时G={1,2,3,4,5,6},未出现的编号最小的点是2,将2和3连边,并删去Prufer序列首项和G中的2。接下来连的边为{4,5},{1,5},{1,3},此时集合G中仅剩3和6,在3和6之间连边,原树恢复。
题解:
由上面我们知道一个无根树可以由
长度的
序列表示,然后每个度为d的节点出现次数为
,那么我们可以知道除掉位置度数节点后的排列数为
,然后在每一个排列中放值第一个节点的方案数为
,放第二个节点的方案数为
,以此类推得到其他的,那么剩余位置度数节点的方案为
,也就是在剩余的位置中排列即可
最后的答案为:
通过约分化简可以得到
即为:
因为n的范围为[1, 1000],所以要涉及到高精度的问题,我用Java大数解决的,巨佬们可以用素数优化一下。
扫描二维码关注公众号,回复:
3970918 查看本文章
import java.util.*;
import java.math.*;
public class Main {
public static void main(String [] args) {
Scanner cin = new Scanner(System.in);
BigInteger zero = BigInteger.ZERO, one = BigInteger.ONE;
BigInteger m = zero;
int []d = new int[1002];
BigInteger []fac = new BigInteger[1002];
fac[0] = one;
for(int i = 1; i < 1002; i++) {
fac[i] = fac[i - 1].multiply(BigInteger.valueOf(i));
}
int n = cin.nextInt(), tot = 0;
for(int i = 1; i <= n; i++) {
d[i] = cin.nextInt();
if(d[i] == -1) {
m = m.add(one);
} else {
tot = tot + d[i] - 1;
}
}
BigInteger fz = fac[n - 2].multiply(m.pow(n - 2 - tot)), fm = fac[n - 2 - tot];
for(int i = 1; i <= n; i++) {
if(d[i] == -1) continue;
fm = fm.multiply(fac[d[i] - 1]);
}
System.out.println(fz.divide(fm));
cin.close();
}
}