1. 程式人生 > >奶牛家譜 Cow Pedigrees

奶牛家譜 Cow Pedigrees

style 前綴和 新的 i++ bubuko for 節點 spa 留言

令人窒息的奶牛題

題目描述

農民約翰準備購買一群新奶牛。 在這個新的奶牛群中, 每一個母親奶牛都生兩個小奶牛。這些奶牛間的關系可以用二叉樹來表示。這些二叉樹總共有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