P2290 [HNOI2004]树的计数【prufer序列】

题意

一个有 \(n\) 个节点的树,设它的节点分别为 \(v_1,v_2,\dots,v_n\),已知第 \(i\) 个节点 \(v_i\) 的度数为 \(d_i\),问满足这样的条件的不同的树有多少棵。

\(1\leq n \leq 150\)

分析

根据 \(\text{prufer}\) 序列的性质,每棵树对应序列的长度均为 \(n-2\),每个编号 \(i\) 会在序列中出现 \(d_i-1\) 次。结合可重集合的排列公式,可得最终结果为:

\[ans=\frac{(n-2)!}{\prod_{i=1}^{n}(d_i-1)!} \]

同时,考虑到无解的情况:

  • 出现点的度大于 \(n-1\) 的情况
  • \(n>1\) 时,出现点的度为 \(0\)
  • 各点的度数之和不等于 \(2(n-1)\) ,即 \(\sum{(d_i-1)} \neq n-2\)

使用高精度或者 \(\text{python}\),即可算出。

代码

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

const ll scale=100000;
struct number
{
    int maxn=1010;
    ll di[1010];
    void clc()
    {
        for(int i=0;i<maxn;i++) di[i]=0;
    }
    int comp(const number &b)const//比较大小
    {
        if(di[0]>b.di[0]) return 1;
        if(di[0]<b.di[0]) return -1;
        for(int i=di[0];i>=1;i--)
        {
            if(di[i]>b.di[i]) return 1;
            if(di[i]<b.di[i]) return -1;
        }
        return 0;
    }
    number operator - (const number &b)const
    {
        number res,a;
        for(int i=0;i<=di[0];i++) a.di[i]=di[i];
        for(int i=1;i<=a.di[0];i++)
        {
            if(a.di[i]<b.di[i])
            {
                a.di[i]+=scale;
                a.di[i+1]--;
            }
            res.di[i]=a.di[i]-b.di[i];
        }
        res.di[0]=a.di[0];
        ll &p=res.di[0];
        while(res.di[p]==0&&p>=0) p--;
        return res;
    }
    number operator *(const number &b)const//高精度乘高精度
    {
        number res;
        for(int i=1;i<=di[0];i++)
        {
            for(int j=1;j<=b.di[0];j++)
                res.di[i+j-1]=di[i]*b.di[j];
        }
        res.di[0]=di[0]+b.di[0];
        for(int i=1;i<=res.di[0];i++)
        {
            res.di[i+1]+=res.di[i]/scale;
            res.di[i]=res.di[i]%scale;
        }
        ll &p=res.di[0];
        while(res.di[p]==0&&p>=0) p--;
        return res;
    }
    number operator / (const number &b)const //高精度除高精度,默认大于
    {
        number res,a;
        res.clc();
        a.clc();
        for(int i=0;i<=di[0];i++) a.di[i]=di[i];
        int f=comp(b);
        while(f>=0)
        {
            a=a-b;
            res.di[1]++;
            int p=1;
            while(res.di[p]>0)
            {
                res.di[p+1]+=res.di[p]/scale;
                res.di[p]=res.di[p]%scale;
                p++;
            }
            res.di[0]=p;
            f=a.comp(b);
        }
        //最终a中存余数
        ll &p=res.di[0];
        while(res.di[p]==0&&p>=0) p--;
        return res;
    }
    void print()
    {
        printf("%lld",di[di[0]]);
        int d=5;//根据具体的进制确定
        for(int i=di[0]-1;i>=1;i--)
            printf("%.5lld",di[i]);
        printf("\n");
    }
};
void mul(number &a,int b)
{
    for(int i=1;i<=a.di[0];i++)
        a.di[i]*=b;
    for(int i=1;i<=a.di[0]+10;i++)
    {
        a.di[i+1]+=a.di[i]/scale;
        a.di[i]=a.di[i]%scale;
    }
    a.di[0]+=10;
    ll &p=a.di[0];
    while(a.di[p]==0&&p>=0) p--;
}
int main()
{
    int n,d,flag=1,tol=0;
    scanf("%d",&n);
    number x,y;
    x.clc();
    y.clc();
    x.di[0]=y.di[0]=1;
    x.di[1]=y.di[1]=1;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&d);
        tol+=d;
        if(d>n-1||(n>1&&d==0)) flag=0;
        for(int j=1;j<d;j++)
            mul(y,j);
    }
    if(tol!=2*(n-1)) flag=0;
    if(flag==0)
    {
        printf("0\n");
        return 0;
    }
    //y.print();
    for(int i=1;i<n-1;i++)
        mul(x,i);
    //x.print();
    number ans=x/y;
    ans.print();
    return 0;
}

posted @ 2020-09-07 14:20  xzx9  阅读(107)  评论(0编辑  收藏  举报