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

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公式
答案就是 (n2)!(d11)!(d21)!...(dn1)!
因为保证结果不超出 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值