LOJ#6495. 「雅礼集训 2018 Day1」树 题解

题目链接

这个题训练的时候做了,结果只有两个人过,而且还有一个写的是状压DP……

(dp_{n,d}) 表示n个点,最大深度为d的二叉树个数.

转移分为两种情况:

1、1号节点只有1个子树。此时的DP值为 (dp_{n-1,d-1}.)

2、1号节点有多于一个子树。由于2号节点的父亲一定是1,我们考虑枚举2号节点所在子树的大小和最大深度然后暴力转移,复杂度 (Theta(N^4).)

精细实现应该可以做到 (Theta(N^3).)

至于求答案下取整的问题,可以考虑使用long double__int128.

代码 :

#include <bits/stdc++.h>
#define LL __int128
using namespace std;
inline void print(LL x){
	if (x > 9) print(x/10); putchar(x%10+'0');
}
inline int power(int x,int y,int P){
	static int r; r = 1;
	while (y){ if (y&1) r = 1ll * r * x % P; x = 1ll * x * x % P; y >>= 1; }
	return r;
}
LL C[25][25],dp[25][25];
int main(){
	int i,j,k,s,t;
	for (i = 0; i <= 24; ++i) for (j = 0; j <= i; ++j) C[i][j] = (i==j || !i || !j) ? 1 : (C[i-1][j-1] + C[i-1][j]);
	dp[1][1] = 1;
	for (i = 2; i <= 24; ++i)
	for (j = 1; j <= 24; ++j){
		dp[i][j] = dp[i-1][j-1];
		for (s = 1; s <= i-2; ++s){
			for (t = 1; t <= s && t < j-1; ++t) dp[i][j] += C[i-2][s-1] * dp[s][t] * dp[i-s][j];
			LL tot = 0;
			for (k = 1; k <= j; ++k) tot += dp[i-s][k];
			dp[i][j] += C[i-2][s-1] * dp[s][j-1] * tot;
		}
	}
	int P,n; cin >> n >> P;
	LL sumd = 0,frac = 1;
	for (i = 1; i <= n; ++i) sumd += i * dp[n][i];
	for (i = 1; i < n; ++i) frac *= i;
	print((sumd * 2 + frac) / frac / 2),putchar('
');
	sumd = sumd % P * power(frac % P,P-2,P) % P;
	print(sumd),putchar('
');
	return 0;
}
原文地址:https://www.cnblogs.com/s-r-f/p/13599396.html