bzoj1005: [HNOI2008]明明的烦恼 prufer序列

https://www.lydsy.com/JudgeOnline/problem.php?id=1005

给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树?

题解:prufer序列,prufer序列是一种无根树的编码表示,对于一棵n个节点带编号的无根树,对应唯一一串长度为n-2的prufer编码。

prufer序列中某个编号出现的次数就等于这个编号的节点在无根树中的度数-1

所以一张n个点的无向完全图有n^(n-2)个生成树(长度为n-2的数列,每个地方有n种放法)

n个节点度依次为d1.d2.......dn,无根树一共有(n-2)!/(d1-1)!*(d2-1)!....*(dn-1)!,   即点的数量固定(di-1),要求放法不一样,全排列除上每种点重复的放法

对于该题,剩余的点left为n-2-di(di!=-1),一共有(n-2)!/(di-1)!*...*left!,  然后left每个有m(di==-1的点)中可以放的点,就是m^left中方法

最后答案就是(n-2)/(di-1)!。。。。*m^(left)

/**************************************************************
    Problem: 1005
    User: walfy
    Language: Java
    Result: Accepted
    Time:1432 ms
    Memory:20500 kb
****************************************************************/
 
import java.math.BigInteger;
import java.util.Scanner;
 
 
public class Main {
 
    /**
     * @param args
     */
    static BigInteger [] fac = new BigInteger [1000+10];
    static void init()
    {
        fac[0] = BigInteger.ONE;
        for(int i=1;i<=1000;i++)
        {
            fac[i]=fac[i-1].multiply(BigInteger.valueOf(i));
        }
    }
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Scanner cin = new Scanner(System.in);
        init();
        int n = cin.nextInt();
        int [] d = new int[1000+10];
        int sum = 0,num = 0;
        BigInteger ans = fac[n-2];
        for(int i=1;i<=n;i++)
        {
            d[i] = cin.nextInt();
            if(d[i]!=-1)
            {
                sum += d[i]-1;
                ans=ans.divide(fac[d[i]-1]);
            }
            else num++;
        }
        int left = n-2-sum;
        ans = ans.divide(fac[left]);
        ans = ans.multiply(BigInteger.valueOf(num).pow(left));
        System.out.println(ans);
    }
 
}
View Code

 

posted @ 2018-04-28 10:01  walfy  阅读(123)  评论(0编辑  收藏  举报