[BZOJ]1089 嚴格n元樹(SCOI2003)

  十幾年前的題啊……果真還處於高精度遍地走的年代。不過經過這道題,小C想mark一下n叉樹計數的作法。編程

Description

  若是一棵樹的全部非葉節點都剛好有n個兒子,那麼咱們稱它爲嚴格n元樹。若是該樹中最底層的節點深度爲d(根的深度爲0),那麼咱們稱它爲一棵深度爲d的嚴格n元樹。例如,深度爲2的嚴格2元樹有三個,以下圖:spa

  

  給出n,d,編程數出深度爲d的n元樹數目。code

Input

  僅包含兩個整數n,d。blog

Output

  僅包含一個數,即深度爲d的n元樹的數目。ip

Sample Input

  3 5get

Sample Output

  58871587162270592645034001string

HINT

  0 < n <= 32,0 <= d <=16,保證答案的十進制位數不超過200位。it

 

Solution

  把題目中樹的邊當作點,點當作邊,題目就轉化爲求深度爲d的n叉樹的個數(根節點深度爲1)。io

  直覺告訴咱們,深度在d之內的樹的個數 比 深度爲d的樹的個數 好求,因此,設f[d]=深度在d之內的樹的個數。ast

  而後 深度爲d的樹的個數 = 深度在d之內的樹的個數 - 深度在d-1之內的樹的個數 = f[d] - f[d-1]。

  然而小C一開始仍是沒有頭緒,開始DP打表觀察規律。

  而後就觀察出了遞推式:f[x] = f[x-1]^n+1 (1<=x<=d , f[0] = 1)。

  仔細想一想爲何呢?咱們用n棵深度在x-1之內的樹做爲兒子,再加上根節點就變成深度在x之內的樹啦!

  最後+1是由於還要加上深度爲0的空樹。

#include <cstdio>
#include <algorithm>
#include <cstring>
#define MOD 10000
#define MS 400
#define MN 20
using namespace std;
struct hp
{
    int len,a[MS];
    void add() {++a[1];}
    friend hp operator-(const hp& A,const hp& B)
    {
        hp C=A;
        register int i;
        for (i=1;i<=B.len;++i)
        {
            C.a[i]-=B.a[i];
            if (C.a[i]<0) --C.a[i+1],C.a[i]+=MOD;
        }
        while (!C.a[C.len]) --C.len;
        return C;
    }
    friend hp operator*(const hp& A,const hp& B)
    {
        hp C; C.len=A.len+B.len+1;
        register int i,j;
        memset(C.a,0,sizeof(C.a));
        for (i=1;i<=A.len;++i)
            for (j=1;j<=B.len;++j)
                C.a[i+j-1]+=A.a[i]*B.a[j];
        for (i=1;i<C.len;++i)
            C.a[i+1]+=C.a[i]/MOD,C.a[i]%=MOD;
        while (!C.a[C.len]) --C.len;
        return C;
    }
}f[MN],ans;
int m,n;

inline int read()
{
    int n=0,f=1; char c=getchar();
    while (c<'0' || c>'9') {if(c=='-')f=-1; c=getchar();}
    while (c>='0' && c<='9') {n=n*10+c-'0'; c=getchar();}
    return n*f;
}

hp mi(hp x,int y)
{
    hp z;
    memset(z.a,0,sizeof(z.a)); z.len=z.a[1]=1;
    for (;y;y>>=1,x=x*x) if (y&1) z=z*x;
    return z;
}

int main()
{
    register int i;
    m=read(); n=read();
    if (n<=1) return 0*printf("1");
    f[1].len=1; f[1].a[1]=2;
    for (i=2;i<=n;++i) f[i]=mi(f[i-1],m),f[i].add();
    ans=f[n]-f[n-1];
    printf("%d",ans.a[ans.len]);
    for (i=ans.len-1;i;--i) printf("%04d",ans.a[i]);
}

 

Last Word

  果真仍是觀察規律好用。

  一道還算不錯的題由於摻了高精度而風評被害。

相關文章
相關標籤/搜索