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

Posted uid001

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了长沙理工大学第十二届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\n", 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]$

 

以上是关于长沙理工大学第十二届ACM大赛-重现赛 大家一起来数二叉树吧 (组合计数)的主要内容,如果未能解决你的问题,请参考以下文章

长沙理工大学第十二届ACM大赛

2021 第十二届蓝桥杯大赛软件赛省赛,C/C++ 大学B组题解

2021 第十二届蓝桥杯大赛软件赛省赛,C/C++ 大学B组题解

2021 第十二届蓝桥杯大赛软件赛省赛(第二场),C/C++大学B组题解

2021 第十二届蓝桥杯大赛软件赛省赛(第二场),C/C++大学B组题解

2021.5.9 第十二届蓝桥杯大赛软件赛省赛第二场大学B组(个人题解)