prufer序列

给一棵树,它的prufer序列由一下步骤得到:

(1)选择度数为1的编号最小的点,把它删掉并把和它相连的点加入序列
(2)重复第一步直到剩下两个点

prufer序列的性质:

长度为n-2,记每个点的度数为di,那么每个点都会在prufer序列中出现di-1次

那么prufer序列有什么用呢?很显然每棵不同的树都对应一个不同的prufer序列,那么当我们遇到一些需要计算不同的树的个数时就可以把问题转化成组合数学计算啦

bzoj1005代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<string>
#include<iomanip>
#include<vector>
#include<set>
#include<map>
#include<queue>

using namespace std;
typedef long long LL;
typedef unsigned long long ULL;

#define rep(i,k,n) for(int i=(k);i<=(n);i++)
#define rep0(i,n) for(int i=0;i<(n);i++)
#define red(i,k,n) for(int i=(k);i>=(n);i--)
#define sqr(x) ((x)*(x))
#define clr(x,y) memset((x),(y),sizeof(x))
#define pb push_back
#define mod 1000000007
int n,m,pcnt,p[2000],a[1010],b[1010],d[1010];
bool vis[1010];
const int maxn=3010;

struct bign
{
    int len,s[maxn];
    bign()
    {
        memset(s,0,sizeof(s));
        len=1;
    }
    bign(int num){*this=num;}
    bign(const char *num){*this=num;}
    bign operator = (const int num)
    {
        if(num==0)
        {
            len=1;
            memset(s,0,sizeof(s));
            return *this;
        }
        char s[maxn];
        sprintf(s,"%d",num);
        *this=s;
        return *this;
    }
    bign operator = (const char *num)
    {
        for(int i=0;num[i]=='0';num++);
        len=strlen(num);
        for(int i=0;i<len;i++)s[i]=num[len-i-1]-'0';
        return *this;
    }
    void clean()
    {
        while(len>1&&!s[len-1])len--;
    }
    bign operator *(const bign &b)
    {
        bign c;
        c.len=len+b.len;
        for(int i=0;i<len;i++)
        {
            for(int j=0;j<b.len;j++)
            {
                c.s[i+j]+=s[i]*b.s[j];
            }
        }
        for(int i=0;i<c.len;i++)
        {
            c.s[i+1]+=c.s[i]/10;
            c.s[i]%=10;
        }
        c.clean();
        return c;
    }
    string str() const
    {
        string res="";
        for(int i=0;i<len;i++)res=char(s[i]+'0')+res;
        return res;
    }
};

ostream& operator << (ostream &out,const bign &x)
{
    out<<x.str();
    return out;
}


bign quipow(bign x,int k)
{
    bign ret=1;
    while(k)
    {
        if(k&1)ret=ret*x;
        x=x*x;
        k>>=1;
    }
    return ret;
}

int main()
{
    pcnt=0;
    for(int i=2;i<=1000;i++)
    {
        if(!vis[i])p[++pcnt]=i;
        for(int j=1;j<=pcnt && i*p[j]<=1000;j++)
        {
            vis[i*p[j]]=1;
            a[i*p[j]]=p[j];
            if(i%p[j]==0)break;
        }
    }

    scanf("%d",&n);
    m=0;
    rep(i,1,n)
    {
        scanf("%d",&d[m+1]);
        if(d[m+1]!=-1)m++;
    }
    if(n<=2)
    {
        if(n==1)puts(d[1]>0?"0":"1");
        else if(n==2)
        {
            bool flag=0;
            rep(i,1,m)if(d[m]!=1)flag=1;
            puts(flag?"0":"1");
        }
        return 0;
    }
    d[m+1]=n-2;
    rep(i,1,m)d[m+1]-=d[i]-1;
    if(d[m+1]<0)
    {
        puts("0");
        return 0;
    }
    rep(i,2,n-2)b[i]++;
    rep(i,1,m)rep(j,2,d[i]-1)b[j]--;
    rep(i,2,d[m+1])b[i]--;
    bign ans=1;
    red(i,n-2,2)
    {
        if(!vis[i])ans=ans*quipow(i,b[i]);
        else
        {
            b[i/a[i]]+=b[i];
            b[a[i]]+=b[i];
        }
    }
    ans=ans*quipow(n-m,d[m+1]);
    cout<<ans<<endl;


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值