题意
计数 (n) 层的满二叉树的简单路径条数,对 (10^9+7) 取模。
(n le 400)
思路
因为每个点的祖先很多,每棵子树会被进入多次,难以直接计算。
考虑分阶段考虑,一颗 (d) 层满二叉树是由 (2) 个 (d-1) 层满二叉树加上根节点拼成的,而根只能经过一次。
要计算 (d) 层的路径条数,发现可能有一个子树中有两条路径,它们经过根,被拼接成一条。
而计算出两条路径的方案又需要三条路径的方案 (cdots cdots) 可以设 (f_d(c)) :(d) 层满二叉树选择 (c) 条点不重复的路径的方案数。
转移需要讨论路径与根的关系。
以下 (leftarrow) 指右边对左边的贡献,(F = f_d, G= f_{d-1})。
- 不过根,(F(p + q) leftarrow G(p)G(q))
- 根作为单独的一条路径,(F(p + q +1) leftarrow G(p)G(q))
- 选取一条路径与根相连,(F(p + q) leftarrow 2(p + q)G(p) G(q)) (两种方向)
- 选取两条路径与根相连,(F(p + q) leftarrow (p + q)(p + q - 1)G(p)G(q))
可以手画一下,长度为 (1) 的路径并不需要特殊处理。
不需要区分与根相连的两条路径是否在同一子树中。某一边没有任何路径也是可以的。
复杂度 (mathcal O(n^3))
实现
#include <cstdio>
#include <algorithm>
using namespace std;
const int M = 1000000007;
inline int add(int x, int y) {return x+y>=M ? x+y-M : x+y;}
inline int mul(int x, int y) {return 1LL * x * y % M;}
template<class ...Args> inline int mul(int x, int y, Args... t){return mul(mul(x, y), t...);}
inline void inc(int &x, int y=1) {x += y; if(x >= M) x -= M;}
const int N = 512;
int f[N][N];
int main(){
int n;
scanf("%d", &n);
f[1][0] = f[1][1] = 1;
for(int d=2; d<=n; d++){
int li = min((1 << min(d, 30)) - 1, n - d + 1);
int lp = min((1 << min(d - 1, 30)) - 1, n - d + 2);
int *F = f[d], *G = f[d - 1];
for(int i=0; i<=lp; i++)
for(int j=0, lj=min({lp, li+1-i, i}); j<=lj; j++){
int p = i == j ? mul(G[i], G[j]) : mul(2, G[i], G[j]);
inc(F[i + j], p);
inc(F[i + j + 1], p);
inc(F[i + j], mul(2, add(i, j), p));
inc(F[i + j - 1], mul(i + j, i + j - 1, p));
}
}
printf("%d
", f[n][1]);
return 0;
}