1. 程式人生 > >【普呂弗編碼 + 組合數學】bzoj 1211: [HNOI2004]樹的計數

【普呂弗編碼 + 組合數學】bzoj 1211: [HNOI2004]樹的計數

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個點,我們可以得到答案
a n s

= A n 2 n
2
i = 1 n ( d u [ i ] 1 ) !
ans=\frac{A_{n-2}^{n-2}}{\prod_{i=1}^{n}(du[i]-1)!}
值得注意的是當只有一個節點時,需要特判,假如這個點度數不為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);
}