hdu 5370 Tree Maker(catalan+dp)

179 篇文章 0 订阅
163 篇文章 3 订阅

题目链接:hdu 5370 Tree Maker


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;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值