zoukankan      html  css  js  c++  java
  • [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)序来转移。

  • 相关阅读:
    uc浏览器开发版
    探索.NET中的事件机制
    “多态枚举”数值如何判断?
    关于“程序集与命名空间”
    AutoResetEvent和ManualResetEvent的异同
    C# 获取DOS命令的返回值
    自定义控件——自绘
    关于using……的一些探讨
    XmlDocument操作xml类
    使用Trigger实现Cascading的功能
  • 原文地址:https://www.cnblogs.com/jz-597/p/11535406.html
Copyright © 2011-2022 走看看