http://www.lydsy.com/JudgeOnline/problem.php?id=1005
题意:
Description
自从明明学了树的结构,就对奇怪的树产生了兴趣......给出标号为1到N的点,以及某些点最终的度数,允许在
任意两点间连线,可产生多少棵度数满足要求的树?
Input
第一行为N(0 < N < = 1000),
接下来N行,第i+1行给出第i个节点的度数Di,如果对度数不要求,则输入-1
Output
一个整数,表示不同的满足要求的树的个数,无解输出0
思路:
又了解了一个神奇的东西,prufer数列!!!
prufer数列,可以用来解一些关于无根树计数的问题。
prufer数列是一种无根树的编码表示,对于一棵n个节点带编号的无根树,对应唯一一串长度为n-1的prufer编码。
(1)无根树转化为prufer序列。
首先定义无根树中度数为1的节点是叶子节点。
找到编号最小的叶子并删除,序列中添加与之相连的节点编号,重复执行直到只剩下2个节点。
如下图的树对应的prufer序列就是3,5,1,3。
具体实现可以用一个set搞定,维护度数为1的节点。复杂度O(nlogn)。
(2)prufer序列转化为无根树。
设点集V={1,2,3,...,n},每次取出prufer序列中最前面的元素u,在V中找到编号最小的没有在prufer序列中出现的元素v,给u,v连边然后分别删除,最后在V中剩下两个节点,给它们连边。最终得到的就是无根树。
具体实现也可以用一个set,维护prufer序列中没有出现的编号。复杂度O(nlogn)。
最后有一个很重要的性质就是prufer序列中某个编号出现的次数就等于这个编号的节点在无根树中的度数-1。
接下来就是按照这个式子计算即可,用java计算会比较方便。
2 3 import java.math.*; 4 import java.util.Scanner; 5 6 public class Main{ 7 static int n; 8 static int d[]=new int[1005]; 9 static BigInteger c[]=new BigInteger[1005]; 10 static BigInteger ans; 11 12 public static void main(String[] args){ 13 Scanner in=new Scanner(System.in); 14 int flag=0, tot=0, cnt=0; 15 while(in.hasNextInt()){ 16 n=in.nextInt(); 17 for(int i=0;i<n;i++){ 18 d[i]=in.nextInt(); 19 if(d[i]==0 || d[i]>n-1) flag=1; 20 if(d[i]==-1) continue; 21 tot+=d[i]-1; 22 cnt++; 23 } 24 25 if(flag==1) {System.out.println("0");continue;} 26 c[0]=BigInteger.valueOf(1); 27 for(int i=1;i<=n;i++) c[i]=c[i-1].multiply(BigInteger.valueOf(i)); 28 ans=c[n-2]; 29 for(int i=1;i<=n-2-tot;i++) ans=ans.multiply(BigInteger.valueOf(n-cnt)); 30 ans=ans.divide(c[n-2-tot]); 31 for(int i=0;i<n;i++){ 32 if(d[i]==-1) continue; 33 ans=ans.divide(c[d[i]-1]); 34 } 35 System.out.println(ans); 36 } 37 in.close(); 38 } 39 }