#高精度,排列组合、dp#JZOJ 2755 树的计数 From 2020.02.01【NOIP提高组】模拟A 组

题目

\(n\)个点直径为\(d\)的标号树个数(多组数据)
\(0\leq d\leq n\leq 50,n>0\)


分析

首先特判一下\(n==d\)无解,\(d=0\)除非只有一个点,\(d=1\)除非只有两个点
然后直径很难搞,考虑把它转换成深度
\(dp[n][h]\)表示\(n\)个点,深度不超过\(h\)的标号树个数
那么\(dp[n][h]=\sum_{i=1}^{n-1}dp[i][h-1]*dp[n-i][h]*c[n-2][i-1]*i\)
解释一下什么意思(未完待续)


代码

#include <cstdio>
#include <cstring>
#define rr register
const int mod=1000000000,N=51; long long c[N][N];
struct Big{long long p[11];}dp[N][N];
Big operator +(Big a,Big b){
    rr int Len=a.p[0]>b.p[0]?a.p[0]:b.p[0],g=0;
    rr Big c; memset(c.p,0,sizeof(c.p));
    for (rr int i=1;i<=Len;++i){
        c.p[i]=a.p[i]+b.p[i]+g;
        if (c.p[i]>=mod) g=1,c.p[i]-=mod;
            else g=0;
    }
    if (g) c.p[++Len]=g;
    c.p[0]=Len;
    return c;
}
Big operator -(Big a,Big b){
    rr int Len=a.p[0]>b.p[0]?a.p[0]:b.p[0],g=0;
    rr Big c; memset(c.p,0,sizeof(c.p));
    for (rr int i=1;i<=Len;++i){
        rr int s=a.p[i]-b.p[i]-g;
        if (s<0) s+=mod,g=1; else g=0;
        c.p[i]=s;
    }
    while (!c.p[Len]) --Len;
    c.p[0]=Len;
    return c;
}
Big operator *(Big a,Big b){
    rr int Len=a.p[0]+b.p[0];
    rr Big c; memset(c.p,0,sizeof(c.p));
    for (int i=1;i<=a.p[0];++i)
    for (int j=1;j<=b.p[0];++j)
        c.p[i+j-1]+=a.p[i]*b.p[j];
    for (rr int i=1;i<=Len;++i) c.p[i+1]+=c.p[i]/mod,c.p[i]%=mod;
    if (!c.p[Len]) --Len;
    c.p[0]=Len;
    return c;
}
inline Big Num_To_Big(long long n){
    rr Big c; memset(c.p,0,sizeof(c.p));
    for (;n;n/=mod) c.p[++c.p[0]]=n%mod;
    return c;
}
inline void zero_print(int ans){
    for (rr int lim=mod/10;lim>1&&lim>ans;lim/=10) putchar(48);
}
inline void print(int ans){
    if (ans>9) print(ans/10);
    putchar(ans%10+48);
}
inline void Big_print(Big ans){
    print(ans.p[ans.p[0]]);
    for (rr int i=ans.p[0]-1;i;--i)
        zero_print(ans.p[i]),print(ans.p[i]);
}
signed main(){
    c[0][0]=1;
    for (rr int i=1;i<N;++i){
        c[i][0]=c[i][i]=1;
        for (rr int j=1;j<i;++j)
            c[i][j]=c[i-1][j-1]+c[i-1][j];
    }
    for (rr int i=0;i<N;++i) dp[1][i]=Num_To_Big(1);
    for (rr int i=2;i<N;++i) dp[i][0]=Num_To_Big(0);
    for (int n=2;n<N;++n)
    for (int d=1;d<N;++d){
        dp[n][d]=Num_To_Big(0);
        if (d>=n) {dp[n][d]=dp[n][d-1]; continue;}
        for (rr int i=1;i<n;++i)
            dp[n][d]=dp[n][d]+Num_To_Big(c[n-2][i-1]*i)*dp[i][d-1]*dp[n-i][d];
    }
    for (rr int n,d;scanf("%d%d",&n,&d)==2;putchar(10)){
        rr int h=d/2; rr Big ans=Num_To_Big(0);
        if (n==d) {putchar(48); continue;}
        if (!d) {putchar(48+(n==1)); continue;}
        if (!h) {putchar(48+(n==2)); continue;}
        if (d&1){
            for (rr int i=h+1;i<=n;++i){
                rr int j=n-i; if (j<=h) break;
                ans=ans+(dp[i][h]-dp[i][h-1])*(dp[j][h]-dp[j][h-1])*Num_To_Big(c[n-2][i-1]*c[n][2]);
            }
        }else{
            ans=(dp[n][h]-dp[n][h-1])*Num_To_Big(n);
            if (h>=2){
                for (rr int i=h;i<n;++i)
                    ans=ans-Num_To_Big(c[n-1][i]*n*i)*dp[n-i][h-1]*(dp[i][h-1]-dp[i][h-2]);
            }
        }
        Big_print(ans);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Spare-No-Effort/p/12286862.html