1211: [HNOI2004]树的计数
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
思路
根据普吕弗编码(关于Pruper Sequence
https://blog.csdn.net/baiyifeifei/article/details/84065899 ),我们已知,任何一棵树,都唯一对应一段长度为n-2的序列,序列中每个点出现的次数都等于其在树中的度数-1,因此假设有n个点,我们可以得到答案
值得注意的是当只有一个节点时,需要特判,假如这个点度数不为0则无法构成树,而点大于1时,不能有点的度数为0,且必须出现的数的次数之和必须等于n-2
AC代码
/**************************************************************
Problem: 1211
User: FlyWhite
Language: C++
Result: Accepted
Time:0 ms
Memory:1292 kb
****************************************************************/
#include<cstdio>
#include<iostream>
#include<cmath>
#include<string>
#include<cstring>
using namespace std;
typedef long long LL;
const int size=160;
int du[size];
int tim[size];
bool prime[size];
int p[size],tot;
void init_prime(int n)
{
tot=0;
fill(prime,prime+size,true);
for(int i=2;i<=n;i++)
{
if(prime[i] ) p[tot++]=i;
for(int j=0;j<tot&&i*p[j]<n;j++)
{
prime[i*p[j]]=false;
if(i%p[j]==0) break;
}
}
}
void solve(int num,int op)
{
for(int i=2;i<=num;i++)
{
int w=i;
for(int j=0;j<tot;j++)
{
if(w%p[j]==0)
while(w%p[j]==0)
{
w/=p[j];
tim[j]+=op;
}
}
}
}
int main()
{
int n;
scanf("%d",&n);
int sum=0;
memset(tim,0,sizeof(tim));
init_prime(n);
for(int i=1;i<=n;i++)
{
scanf("%d",&du[i]);
if(n==1) continue;
if(!du[i])
{
printf("0\n");
return 0;
}
du[i]--;
sum+=du[i];
}
if(n==1) {
if(!du[1]) printf("1\n");
else printf("0\n");
return 0;
}
if(sum!=n-2) {
printf("0");
return 0;
}
solve(n-2,1);
long long ans=1;
for(int i=1;i<=n;i++)
{
if(du[i]>1)solve(du[i],-1);
}
for(int i=0;i<tot;i++)
{
for(int j=1;j<=tim[i];j++)
{
ans*=p[i];
}
}
printf("%lld",ans);
}