长沙理工大学第十二届ACM大赛-重现赛 大家一起来数二叉树吧 (组合计数)

大意: 求n结点m叶子二叉树个数.

直接暴力, $dp[i][j][k][l]$表示第$i$层共$j$节点, 共$k$叶子, 第$i$层有$l$个叶子的方案数, 然后暴力枚举第$i$层出度为1和出度为2的个数来转移.

复杂度虽然看上去是$O(n^6)$, 但实际上去掉多余状态后只有1178917, 可以通过.

#include <iostream>
#include <cstdio>
#define REP(i,a,n) for(int i=a;i<=n;++i)
using namespace std;
typedef long long ll;
const int P = 1e9+7, P2 = 998244353, INF = 0x3f3f3f3f;
const int N = 55;
int n, m;
int dp[N][N][N][N];
int f[N][N];
int fac[N], ifac[N], pow2[N];
ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;}

int main() {
	fac[0]=ifac[0]=pow2[0]=1;
	REP(i,1,50) fac[i]=(ll)fac[i-1]*i%P,ifac[i]=inv(fac[i]),pow2[i]=pow2[i-1]*2%P;
	dp[1][1][1][1] = 1;
	REP(i,1,50) REP(j,i,50) REP(k,1,j) REP(l,1,k) if (dp[i][j][k][l]) {
		REP(ii,0,l) { 
			int t = min({l-ii,(50-ii-j)/2,50-k});
			REP(jj,0,t) {
				int c = (ll)dp[i][j][k][l]*fac[l]%P*ifac[ii]%P*ifac[jj]%P*ifac[l-ii-jj]%P*pow2[ii]%P;
				(dp[i+1][j+ii+2*jj][k+jj][ii+2*jj]+=c)%=P;
			}
		}
		(f[j][k] += dp[i][j][k][l]) %= P;
	}
	for (int n,m; ~scanf("%d%d", &n, &m); ) printf("%d
", f[n][m]);
}

看了其他人题解后发现可以直接$O(n^4)$的$dp$.

记$dp[i][j]$为$i$节点, $j$叶子的方案数, 枚举根节点左右子树的叶子数$x,y$, 就有

$dp[i][j]=sum dp[x][y]dp[i-1-x][y]$

原文地址:https://www.cnblogs.com/uid001/p/10925416.html