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)序来转移。

  • 相关阅读:
    HDU1029 Ignatius and the Princess IV
    UVA11039 Building designing【排序】
    UVA11039 Building designing【排序】
    POJ3278 HDU2717 Catch That Cow
    POJ3278 HDU2717 Catch That Cow
    POJ1338 Ugly Numbers(解法二)
    POJ1338 Ugly Numbers(解法二)
    UVA532 Dungeon Master
    UVA532 Dungeon Master
    POJ1915 Knight Moves
  • 原文地址:https://www.cnblogs.com/jz-597/p/11535406.html
Copyright © 2011-2022 走看看