奶牛家譜 Cow Pedigrees
令人窒息的奶牛題
題目描述
農民約翰準備購買一群新奶牛。 在這個新的奶牛群中, 每一個母親奶牛都生兩個小奶牛。這些奶牛間的關系可以用二叉樹來表示。這些二叉樹總共有N個節點(3 <= N < 200)。這些二叉樹有如下性質:
每一個節點的度是0或2。度是這個節點的孩子的數目。
樹的高度等於K(1 < K < 100)。高度是從根到最遠的那個葉子所需要經過的結點數; 葉子是指沒有孩子的節點。
有多少不同的家譜結構? 如果一個家譜的樹結構不同於另一個的, 那麽這兩個家譜就是不同的。輸出可能的家譜樹的個數除以9901的余數。
輸入輸出格式
輸入格式:
兩個空格分開的整數, N和K。
輸出格式:
一個整數,表示可能的家譜樹的個數除以9901的余數。
題目分析
無腦dfs
我們可以顯而易見地想到dfs做法。給每一個點賦編號,再接著枚舉有無孩子的狀態。然後如果再大力剪枝,就可以驚喜地發現到$n=60$之後就TLE到飛起了。
像dp的假dp
這不是一道dp題嘛……考慮一下轉移,發現每一種樹要麽從同一層轉移過來、要麽從上一層轉移過來。那麽接著存一存每一種樹最下面一層的葉子節點數,再存一存每一種樹方案總數,接著就是這些信息的轉移……搞來搞去我也搞不清這是什麽東西了。
樹形dpⅠ
設$f[i][j]$表示$i$個節點構造出深度為$j$的樹的方案數。顯而易見的是,轉移方程為
其中,即f[i][k]的前綴和。
在轉移的時候,我們只需要枚舉右孩子的節點數,再利用乘法原理乘上左孩子深度為$j-1$(除去根)的方案數。之後再將重復的情況減一下就可以了。
這看上去並沒有什麽問題對吧。
看到這裏大佬您有沒有發現這個dp出鍋了呢。
是!的!這個人畜無害的dp出鍋了!!!
並且截止撰寫這篇博客的時候,依然沒有找出問題在哪裏……
如果大佬您發現了問題,麻煩在評論區留言,謝謝!
樹形dp Ⅱ
XYZ表示dp方程並不用這麽復雜並且表示我的代碼畫風清奇非常鬼畜
好吧,似乎這份是有點又臭又長的感覺
我們以$f[i][j]$表示$i$個節點,$1..j$深度的方案數的前綴和。那麽轉移起來就方便很多;並且得益於dp方程的定義,子樹節點個數是可以枚舉過去的,不存在上面做法方案數乘2的事情,因此也不用管重復的問題了。
1 #include<bits/stdc++.h> 2 const int MO = 9901; 3 int f[203][203]; 4 int n,m; 5 int main() 6 { 7 scanf("%d%d",&n,&m); 8 for (int i=1; i<=m; i++) 9 f[1][i] = 1; 10 for (int i=1; i<=n; i+=2) 11 for (int j=1; j<=m; j++) 12 for (int k=1; k<i; k+=2) 13 (f[i][j]+=f[k][j-1]*f[i-k-1][j-1])%=MO; 14 printf("%d\n",(f[n][m]+MO-f[n][m-1])%MO); 15 return 0; 16 }
這道題真的是挺(wo)有(tai)趣(cai)的(le),這些一步一步的寫法倒也是有很多值得深思的地方。
(似乎為了這題寫的程序數量可以立個記錄之類的?)
END
奶牛家譜 Cow Pedigrees