标签:
n个节点的二叉树种类为Catalan数的第n项
对于一棵子树而言,被移动过的节点就是确定的位置,所以只要知道已经确定位置的K个节点有多少个空孩子指针M,和就该子树下的N个未确定位置的节点,等于是说用N个节点构造M个可为空的子树的种类数。对于整个树的形态数即为若干棵独立的子树形态数的乘积。
定义dp[i][j]为用i个节点构造j棵树的形态数,dp[i][j] = sum{ dp[i-1][j-k] * catalan[k] | 0 ≤ k ≤j }, 用o(n^3)的复杂度预处理出dp数组。然后模拟操作后计算出每个子树的M和N。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const int maxn = 505; const int mod = 1e9 + 7; int dp[maxn][maxn], catalan[maxn]; void preserve () { catalan[0] = catalan[1] = 1; for (int i = 2; i <= 500; i++) { for (int j = 0; j < i; j++) catalan[i] = (catalan[i] + (ll) catalan[j] * catalan[i-j-1]) % mod; } dp[0][0] = 1; for (int i = 1; i <= 500; i++) { for (int j = 0; j <= 500; j++) { for (int k = 0; k <= j; k++) dp[i][j] = (dp[i][j] + (ll) dp[i-1][j-k] * catalan[k]) % mod; } } } int N, M, far[maxn], son[maxn][2], idx[maxn], cnt[maxn], sum[maxn]; inline int newNode (int f) { M++; cnt[M] = son[M][0] = son[M][1] = 0; far[M] = f; return M; } void init () { M = 0; int u = newNode(1), t, k; idx[u] = M; for (int i = 1; i <= N; i++) { scanf("%d", &t); if (t == 0) u = far[u]; else if (t <= 2) { if (son[u][t-1] == 0) { son[u][t-1] = newNode(u); idx[son[u][t-1]] = idx[u]; cnt[idx[u]]--; } u = son[u][t-1]; } else { scanf("%d", &k); son[u][t-3] = newNode(u); cnt[son[u][t-3]] = k - 1; idx[son[u][t-3]] = son[u][t-3]; } } memset(sum, 0, sizeof(sum)); for (int i = 1; i <= M; i++) { if (son[i][0] == 0) sum[idx[i]]++; if (son[i][1] == 0) sum[idx[i]]++; } } int solve () { int ret = 1; for (int i = 1; i <= M; i++) { if (idx[i] != i) continue; ret = (ll) ret * dp[sum[i]][cnt[i]] % mod; } return ret; } int main () { preserve(); int cas = 1; while (scanf("%d", &N) == 1) { init (); printf("Case #%d: %d\n", cas++, solve ()); } return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
hdu 5370 Tree Maker(catalan+dp)
标签:
原文地址:http://blog.csdn.net/keshuai19940722/article/details/47453181