[题解] 路径计数

题意

计数 \(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\n", f[n][1]);
  return 0;
}
posted @ 2020-08-03 23:07  RiverHamster  阅读(310)  评论(0编辑  收藏  举报
\