长沙理工大学第十二届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大赛-重现赛 大家一起来数二叉树吧 (组合计数)的主要内容,如果未能解决你的问题,请参考以下文章
2021 第十二届蓝桥杯大赛软件赛省赛,C/C++ 大学B组题解
2021 第十二届蓝桥杯大赛软件赛省赛,C/C++ 大学B组题解
2021 第十二届蓝桥杯大赛软件赛省赛(第二场),C/C++大学B组题解