题目大意:求最长链为k的二叉树的方案数
由于是要求方案数 所以就是找规律或者dp 由于要mod1e9+7 所以排除了找规律 然后就尝试着用dp去做
我们先考虑树的一半的情况 先将k/2的最长链放置好 叶子处标号为1 则标号为i的点 可以另外接一个深度小于 等于i-1的子树
而dp[i]维护的是到i节点 深度为i的树的方案数 而dp[i]的前n项和则表示深度小于等于i的树的方案数 这里我们用f[i]表示dp[i]的前n项和
从dp[i-1]推到dp[i]需要考虑重复的问题 首先当i结点再接一个深度小于等于i-2 的子树时候 是不会跟dp[i-1]部分冲突的 因为dp[i-1]是深度一定为i-1的方案数
即第一部分dp[i-1]*f[i-2]
而当i结点再接一个深度等于i-1的子树的时候 这个子树的方案数也应为dp[i-1] 且2颗子树的状态一一对应
所以 其叠加后的方案数应该为C(2, dp[i-1]) (2颗子树取不同状态,因为位置可以调换 所以用C )+dp[i-1](2颗子树取相同状态)
这样 我们就得到了dp[i],而对于整棵树的方案 :
当k是偶数时,左右都为dp[k/2] 叠加后的方案数同前文推导一样 应为 C(2 , dp[k/2])+dp[k/2]
当k是奇数时,情况较为复杂,当中心节点外接一个深度严格小于k/2的子树时 只需要左右2部分dp[k/2] 去重一下 还是C(2 , dp[k/2])+dp[k/2]
当中心节点外接一个深度为k/2的子树时 这时中心节点连接了3个dp[k/2]的子树 且其状态一一对应
C(3,dp[k/2])(3颗子树取不同状态,因为位置可以调换 所以用C)+C(2,dp[k/2])*2(3颗子树取2种不同状态,但因为某一种状态是有2颗树的 所以要乘2,例(1,1,2)和(2,2,1)不同)+dp[k/2](3颗子树取相同状态)
由于存在取模运算 C(n,m)中的除法应该用逆元去算
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cctype>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<algorithm>
#include<set>
#define scnaf scanf
#define cahr char
#define bug puts("bugbugbug");
using namespace std;
typedef long long ll;
const int inf=1000000000;
const int maxn=1e5+100;
const int m=1e5;
const int mod=1e9+7;
ll dp[maxn];//深度为i的树的方案数
ll f[maxn];//深度小于等于i的树的方案数
ll inv2=500000004;
ll inv6=166666668;
void init()
{
dp[0]=0;
dp[1]=1;
f[0]=1;
f[1]=2;
for(int i=2;i<=m;i++)
{
dp[i] = dp[i-1] * f[i-2] % mod + dp[i-1] * (dp[i-1] - 1 + mod) % mod * inv2 % mod;
dp[i]=(dp[i-1]+dp[i])%mod;
f[i]=(dp[i] + f[i-1])%mod;
}
}
int main()
{
init();
int n;
while(~scanf("%d",&n) && n)
{
if(n==1)
{
puts("1");
continue;
}
ll zz=dp[n/2];
ll ans=(zz * (zz - 1 + mod) % mod * inv2 % mod + zz)%mod;
if(n%2)
{
ans = ans * f[n/2-1] % mod;
ans =( ans+ zz * (zz-1) %mod * (zz-2) %mod * inv6 % mod +zz * (zz-1) % mod + zz)%mod;
}
printf("%I64d\n",ans);
}
return 0;
}