【Prufer数列/组合数学】[HNOI2008][HYSBZ/BZOJ1005]明明的烦恼

来源:互联网 发布:北京美工培训班学费 编辑:程序博客网 时间:2024/04/26 19:22

题目链接

分析

Prufer数列

生成Prufer数列

由一棵树得到它的 Prufer Sequence 总共需要 n-2 步,每一步都在当前的树中寻找具有最小标号的叶子节点(度为 1),将与其相连的点的标号设为 Prufer Sequence 的第 i 个元素,并将此叶子节点从树中删除,直到最后得到一个长度为 n-2 的 Prufer Sequence 和一个只有两个节点的树。
所以一个树,只能得到一个唯一的 Prufer Sequence。

由Prufer数列生成树

先将所有编号为 1 到 n 的点的度赋初值为 1,然后加上它在 Prufer Sequence 中出现的次数,得到每个点的度
先执行 n-2 步,每一步,选取具有最小标号的度为 1 的点 u 与 Prufer Sequence 中的第 i 个数 v 表示的顶点相连,得到树中的一条边,并将 u 和 v 的度减1
最后再把剩下的两个度为 1 的点连边,加入到树中
所以Prufer Sequence和一个树唯一对应。
得到

定理:一个 Prufer Sequence 和一棵树一一对应

如果还不不清楚,请自行百度

回到这道题

一个点在 Prufer Sequence 出现的次数等于它的度数减一,我们考虑有确定度数的点,给他们编号1~cnt,第i个点的度数为di

sum=i=1cntdi

如果没有不确定度数的点,那么方案数为全排列。
P=sum!cnti=1(di1)

然而有不确定的点那么,这些确定的点会放在n-2个位置中的sum个,所以
C(sumn2)P

剩下的n-2-sum个位置,每个位置都可以从n-cnt个点中任意选择一个来放。
ans=C(sumn2)P(ncnt)n2sum=(n2)!sum!(n2sum)!sum!cnti=1(di1)(ncnt)n2sum=(n2)!(n2sum)!cnti=1(di1)(ncnt)n2sum

用高精度计算,除法不好做,可先分解质因数。

代码

#include<cstdio>#include<algorithm>#include<cmath>#include<cstring>using namespace std;#define MAXN 1000#define MAXLEN 3000int n,d[MAXN+10],p[MAXN+10],pcnt,fcnt[MAXN+10],cnt,sum;bool f[MAXN+10];void prepare(){    int i,j;    for(i=2;i<=n;i++){        if(!f[i])            p[++pcnt]=i;        for(j=1;p[j]*i<=n;j++){            f[i*p[j]]=1;            if(i%p[j]==0)                break;        }    }}struct hp{    int a[MAXLEN+10];    inline hp(){        memset(a,0,sizeof a);    }    inline hp(hp &a){        *this=a;    }    void int2hp(int b){        while(b)            a[++a[0]]=b%10,b/=10;        if(!a[0])            a[0]++;    }    inline hp(int b){        memset(a,0,sizeof a);        int2hp(b);    }    void operator*=(const hp &b){        hp c;        int len=a[0]+b.a[0],i,j;        for(i=1;i<=a[0];i++)            for(j=1;j<=b.a[0];j++)                c.a[i+j-1]+=a[i]*b.a[j];        for(i=1;i<=len;i++){            c.a[i+1]+=c.a[i]/10;            c.a[i]%=10;        }        while(!c.a[len]&&len)            len--;        c.a[0]=len;        *this=c;    }    void print(){        for(int i=a[0];i;i--)            printf("%d",a[i]);    }}ans(1);void Read(int &x){    char c;    bool f=0;    while(c=getchar(),c!=EOF){        if(c=='-')            f=1;        if(c>='0'&&c<='9'){            x=c-'0';            while(c=getchar(),c>='0'&&c<='9')                x=x*10+c-'0';            ungetc(c,stdin);            if(f)                x=-x;            return;        }    }}void read(){    Read(n);    if(n==1){        Read(d[1]);        if(d[1]<=0)            puts("1");        else            puts("0");        exit(0);    }    for(int i=1;i<=n;i++){        Read(d[i]);        if(d[i]>=n||!d[i]){            puts("0");            exit(0);        }        if(d[i]>-1)            sum+=d[i]-1,cnt++;    }    if(sum>n-2){        puts("0");        exit(0);    }}void de_factor(int n,int d){    int i,t=sqrt(n+0.5);    for(i=1;p[i]<=t&&n>1;i++)        while(n%p[i]==0)            fcnt[p[i]]+=d,n/=p[i];    if(n>1)        fcnt[n]+=d;}hp quick_pow(int a,int b){    hp c(a),ret;    ret.a[0]=1,ret.a[1]=1;    while(b){        if(b&1)            ret*=c;        c*=c;        b>>=1;    }    return ret;}void solve(){    int i,j;    for(i=n-2;i>n-2-sum;i--)        de_factor(i,1);    for(i=1;i<=n;i++)        if(d[i]>1)            for(j=d[i]-1;j;j--)                de_factor(j,-1);    for(i=1;i<=pcnt;i++)        ans*=quick_pow(p[i],fcnt[p[i]]);    ans*=quick_pow(n-cnt,n-2-sum);}int main(){    read();    prepare();    solve();    ans.print();}
0 0
原创粉丝点击