您的位置:首页 > Web前端

4000 [BZOJ 1211][HNOI2004]树的计数(prufer编码+组合数学)

2017-03-13 20:21 337 查看

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

Solution

裸·Prufer编码的应用

关于这个问题M67有一篇文章经典证明:Prüfer编码与Cayley公式

答案就是(n−2)!(d1−1)!(d2−1)!...(dn−1)!

因为保证结果不超出1017,可以用分解质因数来做

不知道为什么会有这么多奇怪的特判…

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#define MAXN 150
using namespace std;
int n,d,cnt=0,sum=0;
int pri[MAXN],num[MAXN];
bool jud[MAXN];
void getprime()
{
for(int i=2;i<=n;i++)
{
if(!jud[i])
pri[++cnt]=i;
for(int j=1;j<=cnt&&i*pri[j]<=n;j++)
{
jud[i*pri[j]]=1;
if(!i%pri[j])break;
}
}
}
void add(int n,int m)
{
int i=1;
while(n>1)
{
while(!(n%pri[i]))
n/=pri[i],num[i]+=m;
i++;
}
}
long long Pow(long long a,long long n)
{
long long res=1;
while(n>0)
{
if(n&1)res*=a;
a=a*a;
n>>=1;
}
return res;
}
void print()
{
long long res=1;
for(int i=1;i<=cnt;i++)
if(num[i])res*=Pow(pri[i],num[i]);
printf("%lld",res);
}
int main()
{
scanf("%d",&n);
getprime();
for(int i=1;i<=n-2;i++)
add(i,1);
for(int i=1;i<=n;i++)
{
scanf("%d",&d);
if(!d&&n!=1)
{
printf("0");
return 0;
}
sum+=d-1;
for(int j=1;j<=d-1;j++)
add(j,-1);
}
if(sum!=n-2)printf("0");
else if(n==1)printf("1");
else print();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: