【bzoj1284】【HNOI2004】【树的计数】【组合数学+prufer数列】

来源:互联网 发布:telnet 端口打开失败 编辑:程序博客网 时间:2024/05/22 14:40

Description

一个有n个结点的树,设它的结点分别为v1, v2, …, vn,已知第i个结点vi的度数为di,问满足这样的条件的不同的树有多少棵。给定n,d1, d2, …, dn,编程需要输出满足d(vi)=di的树的个数。

Input

第一行是一个正整数n,表示树有n个结点。第二行有n个数,第i个数表示di,即树的第i个结点的度数。其中1<=n<=150,输入数据保证满足条件的树不超过10^17个。

Output

输出满足条件的树有多少棵。

Sample Input

4
2 1 2 1

Sample Output

2
题解:首先需要知道有个东西叫prufer数列。然后可以发现一个节点在prufer数列中出现的次数是这个节点的度数减一。这样我们就知道这个数列中有哪些数了。因为一个prufer数列唯一对应一颗树。然后问题就变成了求有多少种prufer数列。又因为我们知道了元素种类与出现次数。于是问题就变成了求一个有重复元素的全排列。
因为n最大有150.所以分解一下质因数就好了。
注意一开始要先判断是否有解。即把所有点的度数减一然后加起来,如果不等于n-2就无解。
#include<cstdio>using namespace std;int n,tot,p[2][151],x;long long ans(1);void cal(int x,int k){     for (int i=2;;i++)     {        if (x%i==0) while (x%i==0) {p[k][i]++;x/=i;}        if (x==1) break;     }}int main(){    scanf("%d",&n);    for (int i=1;i<=n;i++)      {       scanf("%d",&x);       if (x>2)         {          for (int j=1;j<=x-1;j++) cal(j,1);        }       if (x>1) tot+=x-1;      }    if (n==1) {printf("%d",!x);return 0;}    if(tot!=n-2) {printf("0");return 0;}     for (int i=2;i<=tot;i++) cal(i,0);    for (int i=1;i<=150;i++)     if (p[0][i]-p[1][i]>0)       for (int j=1;j<=p[0][i]-p[1][i];j++)         ans*=(long long)i;    printf("%lld",ans);}



0 0
原创粉丝点击