十幾年前的題啊……果真還處於高精度遍地走的年代。不過經過這道題,小C想mark一下n叉樹計數的作法。編程
若是一棵樹的全部非葉節點都剛好有n個兒子,那麼咱們稱它爲嚴格n元樹。若是該樹中最底層的節點深度爲d(根的深度爲0),那麼咱們稱它爲一棵深度爲d的嚴格n元樹。例如,深度爲2的嚴格2元樹有三個,以下圖:spa
給出n,d,編程數出深度爲d的n元樹數目。code
僅包含兩個整數n,d。blog
僅包含一個數,即深度爲d的n元樹的數目。ip
3 5get
58871587162270592645034001string
0 < n <= 32,0 <= d <=16,保證答案的十進制位數不超過200位。it
把題目中樹的邊當作點,點當作邊,題目就轉化爲求深度爲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]); }
果真仍是觀察規律好用。
一道還算不錯的題由於摻了高精度而風評被害。