• [JZOJ6353] 【NOIP2019模拟】给


    题目

    题目大意

    对于所有的整数(k in [1,n]),求叶子结点有(k)个的二叉树个数,满足每个非叶子结点都有两个儿子,并且对于每个叶子结点,从根节点到它经过的向左的边数少于等于(m)个。


    思考历程

    很容易推出这样的(DP)
    (f_{i,j})表示(m=i)(n=j)的答案是多少。
    (f_{i,j}=sum_{k in [1,n)}{f_{i-1,k}f_{i,j-k}})
    这样当然过不了。
    然而,如果只看第二维,就会感觉它和卡特兰数长得很像。
    于是就往打表的方面想……
    打出一个表,表可以分成上下两个三角形,下面的那个三角形就是标准的卡特兰数……
    然而找不到上面的规律。
    %%%GMH大佬居然找到了。
    于是就暴力了……


    正解

    换一种思路(DP)
    (f_{i,j})表示已经放了(i)个节点,从根往下走已经向左走了(j)次。
    有两种转移:
    一个是继续向左走,那就是转移到(f_{i+1,j+1})
    另一个是回到第一个左转的地方,放一个右儿子,那就是转移到(f_{i+1,j-1})
    很显然放的节点个数为(2k-1),所以这个方法是能过的。


    代码

    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 5010
    #define mo 998244353
    #define ll long long
    int m,n;
    ll f[N*2][N];
    int main(){
    	freopen("ca.in","r",stdin);
    	freopen("ca.out","w",stdout);
    	scanf("%d%d",&m,&n);
    	f[1][0]=1;
    	for (int i=1;i<2*n-1;++i)
    		for (int j=0;j<m;++j)
    			if (f[i][j]){
    				(f[i+1][j+1]+=f[i][j])%=mo;
    				if (j)
    					(f[i+1][j-1]+=f[i][j])%=mo;
    			}
    	for (int i=1;i<=n;++i)
    		printf("%lld
    ",f[2*i-1][0]);
    	return 0;
    }
    

    总结

    在做树一类的(DP)的时候,不要仅仅是想着从下往上转移,还要考虑一下按照(dfs)序来转移。

  • 相关阅读:
    C#中使用MATLAB
    各种插值法及效果图对比
    Visual Studio中你所不知道的智能感知
    C#中使用Surfer
    XCode中连接使用GitHub
    个人作业Week1
    个人作业Week3
    个人作业Week2
    第一次作业——subway
    执行ajax加载页面中的js总结
  • 原文地址:https://www.cnblogs.com/jz-597/p/11535406.html
Copyright © 2020-2023  润新知