树的计数(prufer序列 或 purfer序列)

 题解

首先我们要知道一条性质,prufer序列中的某个点出现次数为该点在树中度数-1

感性理解一下,其实按照prufer序列求法自己推一下就出来了

设题目里给的度为$d[]$

先将所有的d--

然后按照排列组合得出来

这是多重集排列数

首先从n-2中选择d[1]个数是$C_{n}^{d[1]}$然后再从剩余n-d[1]中选d[2] $C_{n-d[1]}^{d[2]}$依次类推

$C_{n-2}^{d[1]}\times C_{n-2-d[1]}^{d[2]}\times C_{n-2-d[1]-d[2]}^{d[3]}\times ……\times C_{n-2-d[1]-……-d[n-1]}^{d[n]}$

得到

$\frac{(n-2)!}{\sum\limits_{i=1}^{n}d[i]!}$

高精转移就完了

还是过不了?

一些特判:

首先该题会有无解的情况

然后当只有一个点时方案数为1

然后当出现度数为0的点时方案数要特殊处理

以下是本人丑陋的代码

#include<bits/stdc++.h>
#define ll long long
#define N 10
#define P 1
using namespace std;
ll n,m,d[20000],cnt=0;
bool flag[20000];
struct bignum
{
    ll n[200000],l;
    bignum(){l=1,memset(n,0,sizeof(n));}
    void clear(){while(l>1&&!n[l-1]) l--;}
    void print()
    {
        printf("%lld",n[l-1]);
        for(ll i=l-2;i>=0;i--)
        printf("%0*lld",P,n[i]);
        printf("\n");
    }
    bignum operator = (ll x)
    {
        l=0;    
        while(x)
        {
            n[l++]=x%N;
            x/=N;
        }
        return *this;
    }
    bignum operator +(bignum x) const
    {
        bignum t=*this;
        if(x.l>t.l) t.l=x.l;        
        for(ll i=0;i<t.l;i++)
        {
            t.n[i]+=x.n[i];
            if(t.n[i]>=N)
            {
                t.n[i+1]+=t.n[i]/N;
                t.n[i]%=N;
            }
        }
        return t;
    }
       bignum operator * (const ll& b)
      {
        bignum c;
           c.l=0;
        for(ll i=0,g=0;g||i<l;i++)
        {
            ll x;
            if(i<l)x=n[i]*b+g;
            else x=g;
            c.n[c.l++]=x%N;
            g=x/N;
        }
        return c;
    }
    bignum operator *(bignum x) const
    {
        bignum t=*this,tep;
        tep.l=t.l+x.l+1;
        for(ll i=0;i<t.l;i++)
            for(ll j=0;j<=x.l;j++)
            {
                tep.n[i+j]+=t.n[i]*x.n[j];
            }
        for(ll i=0;i<tep.l;i++)
        {
            tep.n[i+1]+=tep.n[i]/N;
            tep.n[i]%=N;
        }
        tep.clear();
        return tep;
    }
    bool operator <(bignum x) const
    {
        bignum t=*this,tep;
        if(t.l!=x.l)    return t.l<x.l;
        for(ll i=t.l-1;i>=0;i--)
        {
            if(t.n[i]!=x.n[i]) return t.n[i]<x.n[i];
        }
        return 0;
    }
    bool operator >(bignum x) const
    {
        bignum t=*this;
        if(t.l!=x.l) return t.l>x.l;
        for(ll i=t.l-1;i>=0;i--)
        {
            if(t.n[i]!=x.n[i]) return t.n[i]>x.n[i];
        }
        return 0;
    }
    bignum operator -(bignum x) const
    {
        bignum t=*this;
        if(t<x) printf("-"),swap(t,x);
        ll jie=0;
        for(ll i=0;i<t.l;i++)
        {
            t.n[i]-=x.n[i];
            while(t.n[i]<0)
            {
                t.n[i]+=N;
                jie++;
            }
            t.n[i+1]-=jie;
            jie=0;;
        }
        t.clear();
        return t;
    }
    bignum operator /(const ll &x)
    {
        bignum t=*this,r;
        ll tmp=0;
        r.l=t.l;
        for(ll i=t.l-1;i>=0;i--){
            tmp+=t.n[i];
            if(tmp>=x){
                r.n[i]=tmp/x;
                tmp%=x;
            }
            tmp*=N;
        }
        r.clear();
        return r;
    }
}ans;
bignum jie(ll x)
{
    bignum t;t=1;
    for(ll i=2;i<=x;i++){
        t=x*i;
    }
    return t;
}
int main()
{
    memset(flag,0,sizeof(flag));
    ll sum=0,you0=0;
    scanf("%lld",&n);
    for(ll i=1;i<=n;i++)
    {
        scanf("%lld",&d[i]);
        if(d[i])flag[i]=1,cnt++;
        else you0=1;
        d[i]--,sum+=d[i];
        
    }
    if(you0&&n==1){
        cout<<1<<endl;
        return 0;
    }
    if(sum!=n-2||you0) 
    {
        cout<<0<<endl;
        return 0;
    }
    ans=1;
    for(ll i=2;i<=cnt-2;i++)
        ans=ans*i;
    for(ll i=1;i<=n;i++){
        if(flag[i])
        for(ll j=2;j<=d[i];j++)
                ans=ans/j;
    }
    ans.print();
}

 

转载于:https://www.cnblogs.com/znsbc-13/p/11222262.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值