1005: [HNOI2008]明明的烦恼[prufer序列+组合数学]

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/m0_38081836/article/details/83754528

题意:有一个n个点的无根树,已经知道了它每个点的度数(也有可能没有限制),询问有多少种形式的无根树满足上述条件。


前置技能:

prufer序列

定义
任何一个无根树都可以由一个 p r u f e r prufer 序列来唯一表示,其中度为 d d 的节点在序列中出现 d 1 d - 1 次。
构造 p r u f e r prufer 序列:
首先选择节点序号最小的叶子节点,然后将与它相连的节点加入序列,然后把它自己删掉,直到树剩下只有两个节点为止。
e x a m p l e example:
转自百度百科
如上图所以我们求他的prufer序列:

  1. 选择最小的叶子节点 2 2 然后将与它相连的点加入序列,得到 { 3 } \{3\}
  2. 然后删除 2 2 ,重复第一步得到 { 3 , 5 } \{3, 5\}
  3. 直到最后剩下 3 , 6 3,6 ,得到prufer序列 { 3 , 5 , 1 , 3 } \{3, 5, 1, 3\}
    还原

仍为上面的树,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之间连边,原树恢复。


题解:
由上面我们知道一个无根树可以由 n 2 n-2 长度的 p r u f e r prufer 序列表示,然后每个度为d的节点出现次数为 d 1 d - 1 ,那么我们可以知道除掉位置度数节点后的排列数为 C n 2 t o t C_{n - 2}^{tot} ,然后在每一个排列中放值第一个节点的方案数为 C t o t d [ 1 ] 1 C_{tot}^{d[1] - 1} ,放第二个节点的方案数为 C t o t ( d [ 1 ] 1 ) d [ 2 ] 1 C_{tot - (d[1] - 1)}^{d[2] - 1} ,以此类推得到其他的,那么剩余位置度数节点的方案为 m n 2 t o t m^{n - 2 - tot} ,也就是在剩余的位置中排列即可

最后的答案为:

a n s = ( n 2 ) ! ( n 2 t o t ) ! t o t ! t o t ! ( t o t ( d [ 1 ] 1 ) ) ! ( d [ 1 ] 1 ) ! ( d [ n ] 1 ) ! ( d [ n ] 1 ) ! 0 ! m n 2 t o t ans = \frac{(n - 2)!}{(n - 2 - tot)! * tot!} * \frac{tot!}{(tot - (d[1] - 1))!*(d[1] - 1)!} *\dots* \frac{(d[n] - 1)!}{(d[n] - 1)!*0!} * m^{n - 2 - tot}
通过约分化简可以得到

a n s = ( n 2 ) ! ( n 2 t o t ) ! 1 ( d [ 1 ] 1 ) ! 1 ( d [ n ] 1 ) ! m n 2 t o t ans = \frac{(n - 2)!}{(n - 2 - tot)! } * \frac{1}{(d[1] - 1)!} *\dots* \frac{1}{(d[n] - 1)!} * m^{n - 2 - tot}

即为:
a n s = ( n 2 ) ! ( n 2 t o t ) ! Π i = 1 n 1 ( d [ i ] 1 ) ! m n 2 t o t ans = \frac{(n - 2)!}{(n - 2 - tot)! } * \Pi_{i = 1}^{n}{\frac{1}{(d[i] - 1)!}} * m^{n - 2 - tot}

因为n的范围为[1, 1000],所以要涉及到高精度的问题,我用Java大数解决的,巨佬们可以用素数优化一下。


a c   c o d e : ac\ code:


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();
		
	}
}

猜你喜欢

转载自blog.csdn.net/m0_38081836/article/details/83754528