感谢学弟贡献的精彩人类智慧
题目大意
计数$1 le k le n$个节点的除叶子节点外所有节点均有两个儿子,且任意叶子节点到根的左偏距离$le m$的二叉树方案数.
$n,m le 5000$
题目分析
做法一:带性质的序列转化
考虑一下这些叶子的左偏距离有没有什么性质
首先把叶子从左到右写成一个序列。初看上去相邻两个权值的变化没有什么规律,但是仔细思考一下就会发现因为每一个左叶子都有对应的右叶子,因此对相邻的元素有关系$w_L le w_R+1$。并且这样构造出来的序列是和合法的二叉树一一对应的。
于是接下来dp构造这个序列。$f[i][j]$表示构造完了$1cdots i$,现在$i$位置权值为$j$的合法方案数。最终的答案就是$f[i][0]$.
1 #include<bits/stdc++.h> 2 #define MO 998244353 3 const int maxn = 5035; 4 5 int n,m,f[maxn][maxn],sum[maxn]; 6 7 int main() 8 { 9 scanf("%d%d",&m,&n), --m; 10 for (int i=0; i<=m; i++) f[1][i] = 1; 11 for (int i=2; i<=n; i++) 12 { 13 for (int j=1; j<=m; j++) 14 sum[j] = (sum[j-1]+f[i-1][j])%MO; 15 for (int j=0; j<=m; j++) 16 f[i][j] = (f[i][j]+sum[std::min(m, j+1)])%MO; 17 } 18 for (int i=1; i<=n; i++) printf("%d ",f[i][0]); 19 return 0; 20 }
做法二:类比卡特兰数模型
考虑建模转化这一个问题
首先如果一点附加条件都没有,这即是二叉树的计数问题,应当要想到卡特兰数。
那我们就类比卡特兰数的平面直角坐标系带限制走路问题来考虑。
这是类似dfs树的一个优先走左儿子的过程,定义每一次向左儿子(并非只对左叶子)走就是在坐标系上走$ exttt{(1,1)}$的一步;从左儿子回溯向上就是在坐标系上走$ exttt{(0,-1)}$的一步。
以上面这棵树为例,构造出来的坐标系是这样的。
得益于这棵“左右儿子对应存在”的二叉树特殊性质,拥有$n$个叶子节点的树中恰好会有$n$个左儿子,也就是说坐标系上最后的终点是$ exttt{(n,0)}$.而图像里每一个拐点即对应有着极大值的左叶子。
这个构造的性质非常好,由此我们发现了“左偏距离$le m$”的所有方案即和“图像上所有拐点纵坐标$<m$”的情况一一对应。所以之后便可以朴素而直观地dp处理$f[i][0]$表示到达$ exttt{(i,0)}$的方案数量。
1 #include<bits/stdc++.h> 2 #define MO 998244353 3 const int maxn = 5035; 4 5 int m,n,f[maxn][maxn]; 6 7 int main() 8 { 9 scanf("%d%d",&m,&n), --m; 10 f[0][0] = 1; 11 for (int i=1; i<=n; i++) 12 for (int j=m; j>=0; j--) 13 { 14 int c = 0; 15 if (j) c += f[i-1][j-1]; 16 if (j!=m) c += f[i][j+1]; 17 f[i][j] = (f[i][j]+c)%MO; 18 } 19 for (int i=0; i<n; i++) printf("%d ",f[i][0]); 20 return 0; 21 }
END