星星之火

[正睿OJ] #82. 【17 提高 1】 给 解题报告 (DP模仿dfs构造树)

背景描述

对于任意 1kn, 求有多少个左右区分的恰有 k 个叶子节点的二叉树, 满足对于每个节点要么没有叶子节点要么有两个节点, 同时不存在一个叶子节点, 使得根到它的路径上有不少于 m 条向左的边。

你只需要求出答案对 998244353取模的结果。

输入格式

输入共一行,两个正整数 m, n

输出格式

输出 n 行每行一个整数, 第 ii 行输出恰有 ii 个叶子节点的时候的答案对 998244353998244353 取模的结果。

样例输入

3 5

样例输出

1
1
2
4
8

数据规模和约定

对于 20% 的数据, n,m8

对于 35% 的数据, n,m300

对于 60% 的数据, n,m1500

对于 100的数据, 1n,m,5000

 

题解:要想做到 O(N2), 我们需要设计一个 dp 状态, 我们考虑模拟树的 dfs 来 构造这棵树, 我们令 dp[x][i]表示我们当前有了 x 个叶子, 根到当前节点向左走了 i 次的方案数。 那么考虑下一个节点, 要么是当前节点的左儿子, 要么是最后一次向左走的节点的右儿子。

对于黑体字的解释:由于我们是模拟 dfs 来构造这棵树,那么其实我们每次就是对类似下方这个东西操作

对于画圈的那一部分是已经构造好的(1不一定是根节点),当前节点是4

因为我们是从左到右构造,所以我们有两种转移,第一种是加入当前节点的左节点(先加入左节点在考虑右节点,之前说了我们是从左到右构造),第二种是对最后一个有向左的边的且没有右儿子节点加入他的右儿子

我们现在要加入一个节点。

考虑第一种转移:给节点4加入左儿子5,发现叶子节点数不变,当前节点5到根的向左路径数是原来4到根的向左路径数加1,dp[i][j]+=dp[i][j-1]

考虑第二种转移:给节点2加入右儿子5,发现叶子节点数减一,当前节点5到根的向左路径数是原来4到根的向左路径数减1,dp[i][j]+=dp[i-1][j+1]

思考对于叶子节点数为k我们的答案是什么?dp[k][0]。

为什么?考虑到我们当前已经有了 k 个叶子节点,现在的状态肯定是回到了根节点完成了一棵树的形式(可以想象dp[i][0]是从什么地方转移过来的,显然是第二种转移)

下面是AC代码:

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<iostream>
using namespace std;

const int N=5000+15;
const int mod=998244353;
int n,m;
int dp[N][N];
int main()
{
    scanf("%d%d",&m,&n);
    dp[1][0]=1;
    for (int i=1;i<=n;i++)
        for (int j=0;j<m;j++)
        {
            if (j) dp[i][j]+=dp[i][j-1];
            dp[i][j]+=dp[i-1][j+1];
            dp[i][j]%=mod;
        }
    for (int k=1;k<=n;k++) printf("%d\n",dp[k][0]);
    return 0;
}

 

posted @ 2018-08-23 17:15  星星之火OIer  阅读(314)  评论(0)    收藏  举报