HDU-5136 2014ICPC廣州現場賽J - Yue Fei's Battle
阿新 • • 發佈:2018-11-10
求直徑為k、每個點的度不超過3的不同構樹的數目。
考慮按照直徑所在的鏈分為若干部分,顯然每部分都是一棵二叉樹。dp[i]為深度為i的不同構樹的數目,sum[i]為num[i]的字首和。
對於深度為i時,根的兩個分支有可能為:
(1)一個深度為i-1,另一個深度小於i-1,有dp[i-1]*sum[i-2]種方案。
(2)深度都為i-1,兩分支不同時為dp[i-1]*(dp[i-1]-1)/2種,相同時為dp[i-1]種。
即:
考慮直徑為k時最終的答案。可以有如下的討論:
k為偶數時,兩邊的樹的深度都為k/2,顯然此時答案即為
k為奇數時,中間的點上有3個分支,而且這之中最少有兩個分支的深度為k/2。
(1)另外一個的深度小於k/2時:
(2)3個分支深度都為k/2:
都相同:,兩個相同:,互不相同:
即:k為奇數的答案為
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <cmath> using namespace std; typedef long long ll; const int maxn = 100050; const ll mod = 1e9 + 7; int n; ll dp[maxn], num[maxn], sum[maxn]; ll qpow(ll a, ll x) { ll res = 1; while(x) { if(x & 1) res = (res*a) % mod; a = (a*a) % mod; x >>= 1; } return res; } void init() { ll inv2 = qpow(2, mod - 2), inv6 = qpow(6, mod - 2); num[0] = num[1] = sum[0] = 1, num[2] = sum[1] = 2, sum[2] = 4; for(int i = 3;i < maxn;i++) { num[i] = (num[i-1] + 1)*num[i-1]%mod*inv2%mod; num[i] = (num[i] + num[i-1]*sum[i-2]%mod) % mod; sum[i] = (sum[i-1] + num[i]) % mod; } dp[1] = dp[2] = 1; for(int i = 3;i < maxn;i++) { ll tmp = num[i/2]*(num[i/2] + 1)%mod*inv2%mod; if(i % 2 == 0) dp[i] = tmp; else { dp[i] = sum[i/2 - 1]*tmp%mod; dp[i] = (dp[i] + num[i/2]*(num[i/2]-1+mod)%mod + num[i/2]) % mod; dp[i] = (dp[i] + num[i/2]*(num[i/2]-1+mod)%mod*(num[i/2]-2+mod)%mod*inv6%mod) % mod; } } } int main() { init(); while(scanf("%d", &n)) { if(n==0) break; printf("%lld\n", dp[n]); } return 0; }