求直径为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;
}