传送门
题意:求直径上有K个点的不同构树个数(每个点度不超过3)。
二叉树满足每个点度不超过3,把直径从中间切开,两边就是二叉树了。
设
dp[i]=深度为i的不同构二叉树个数。
sum[i]=深度不超过i的不同构二叉树个数。
那么二叉树的两个分支有3种情况:
一个分支深度为i−1,另一个分支深度小于i−1,有dp[i−1]∗sum[i−2]种方法;
两个分支深度都是i−1:
两个分支不一样,有dp[i−1]∗(dp[i−1]−1)/2种方法;
两个分支一样,有dp[i−1]种方法。
dp[i]=dp[i−1]∗sum[i−2]+dp[i−1]∗(dp[i−1]+1)/2
i=K/2
当K为偶数时,两边的二叉树深度都是K/2,
考虑两边可能相同,可能不同,方案数为dp[i]∗(dp[i]+1)/2;
当K为奇数时,中间的点连接3个分支,至少有2个分支深度为K/2。
当3个分支有一个分支深度小于K/2,方案数dp[i]∗(dp[i]+1)/2∗sum[i−1]
当3个分支深度都是K/2:
当3个分支都相同,方案数dp[i]
当3个分支2个相同,方案数为dp[i]∗(dp[i]−1)
当3个分支互不相同,方案数C(dp[i],3)
所有情况加起来就行了。
#include <bits/stdc++.h>
using namespace std;
#define prt(k) cerr<<#k" = "<<k<<endl
typedef long long ll;
const ll mod = 1e9 + 7;
ll pmod(ll a, ll n)
{
ll ret = 1;
for (; n; n>>=1, a=a*a%mod)
if (n&1)
ret = ret * a % mod;
return ret;
}
ll getinv(ll a)
{
return pmod(a, mod - 2);
}
const int N = 100100;
ll dp[N + 5], sum[N + 5];
const ll inv2 = getinv(2);
ll sub(ll a, ll b)
{
return (a - b + mod) % mod;
}
void init()
{
dp[0] = 1;
dp[1] = 1;
sum[0] = 1;
sum[1] = 2;
for (int i=1;i<N;i++)
{
dp[i+1] = (dp[i] * sum[i-1] % mod);
dp[i+1] = (dp[i+1] + (dp[i] + 1)%mod * dp[i] % mod * inv2 % mod) % mod;
sum[i+1] = (sum[i] + dp[i+1]) % mod;
}
}
ll C3(ll a)
{
return ((a*sub(a,1)%mod) * sub(a,2) % mod) * getinv(6) % mod;
}
ll f(int K)
{
int i = K / 2;
ll ans = ((dp[i] * (dp[i] + 1)%mod) % mod * inv2 % mod ) % mod;
ll tmp = ans;
if (K%2==0) {
return ans;
}
ans = (ans * sum[i-1]) % mod;
ans = (ans + dp[i]) % mod;
ans = (ans + (dp[i]-1+mod)%mod * dp[i] % mod) % mod;
ans = (ans + C3(dp[i])) % mod;
return ans;
}
int main()
{
int K;
init();
while (cin>>K, K)
{
ll ans = f(K);
cout<<ans<<endl;
}
return 0;
}