• BZOJ3057: 圣主的考验


    Description

    若对于二叉树T的每个节点v,其左子树的高度L和右子树的高度R均满足|L – R|≤1,则这个树T有可能来自超自然之界。规定若某节点子树为空,则该子树的高度是0。你的任务是求有N个节点的可能来自超自然之界的树的数目。

    题目:http://www.lydsy.com/JudgeOnline/problem.php?id=3057

    题解:
    搬运题解,实在是神优化。。。

    裸的树形DP很简单,F[i,j]表示i个节点,高度为j的方案数。枚举左子树有多少个节点转移即可。

    但是这样是O(N^3)的,会超时。

    注意到满足题目所述要求的树高度的范围很小。不妨把具有i个节点的满足题目要求的树的高度范围记为L[i]~R[i]。

    那么我们先考虑如下两个问题:

    高度为h的树最多有多少个节点。显然就是这个高度的满二叉树的节点个数。

    即:max[h]=max[h-1]*2+1;

    高度为h的树最少有多少个节点。画出前几项之后可以发现就是把h-1和h-2两个树分别作为h的左右子树。

    即:min[h]=min[h-1]+min[h-2]+1;

    求出max、min这两个数组之后,转化一下,扫描一遍即可求出L、R。

    由于L[i]~R[i]的范围非常小,那么我们预处理3000以内的答案,然后O(1)回答即可。

    applepi特意安排了一个trick——输出问题。答案不足九位,要直接输出。多于九位,要补足前导零。这个可以在本地打表找到分界点,大概是37~38这里是分界点。。。

    代码:直接copy了。。。

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <cstdlib>
     4 #include <algorithm>
     5 using namespace std;
     6 
     7 typedef long long ll;
     8 const int N = 3000, mod = 1000000000;
     9 int maxh[31], minh[31], L[N + 10], R[N + 10];
    10 ll dp[N + 10][31], ans[N + 10]; 
    11 
    12 int main ()
    13 {
    14  freopen("domine.in", "r", stdin), freopen("domine.out", "w", stdout);
    15  dp[0][0] = dp[1][1] = 1, ans[1] = 1;
    16  minh[1] = 1, minh[2] = 2;
    17  for (int i = 3; i <= 30; i++) minh[i] = minh[i - 1] + minh[i - 2] + 1;
    18  for (int i = 1; minh[i] <= N && i <= 30; i++)
    19   for (int j = minh[i]; j < min(minh[i + 1], N + 1); j++) R[j] = i;
    20  maxh[1] = 1;
    21  for (int i = 2; i <= 30; i++) maxh[i] = maxh[i - 1] * 2 + 1;
    22  L[1] = 1;
    23  for (int i = 2; maxh[i - 1] <= N && i <= 30; i++)
    24   for (int j = maxh[i - 1] + 1; j <= min(maxh[i], N); j++) L[j] = i;
    25  for (int i = 2; i <= N; i++)
    26  {
    27   for (int l = 0; l < i; l++)
    28   {
    29    int r = i - 1 - l;
    30    for (int h = max(0, L[i] - 2); h <= min(29, R[i] - 1); h++)
    31    {
    32     if (h) dp[i][h + 1] += dp[l][h] * dp[r][h - 1];
    33     (dp[i][h + 1] += dp[l][h] * dp[r][h]) %= mod;
    34     dp[i][h + 2] += dp[l][h] * dp[r][h + 1];
    35    }
    36   }
    37   for (int h = 1; h <= min(29, i); h++)
    38    ans[i] += dp[i][h];
    39   ans[i] %= mod;
    40  }
    41  int n; while (scanf("%d", &n), n)
    42   printf(n >= 38 ? "%09lld
    " : "%lld
    ", ans[n]);
    43  fclose(stdin), fclose(stdout);
    44  return 0;
    45 }
    View Code

     

  • 相关阅读:
    [Leetcode][Python][DP]Regular Expression Matching
    [LeetCode][Python]Container With Most Water
    [LeetCode][Python]Regular Expression Matching
    [LeetCode][Python]Palindrome Number
    [LeetCode][Python]Largest Number
    前后端数据交互的几个方法
    AngularJS中服务和自定义服务的常见方式及特点
    uniapp解决图形验证码问题及arraybuffer二进制转base64格式图片
    动态面包屑组件(适合嵌套路由)
    vue + antd-vue + 腾讯云点播 完成视频上传功能
  • 原文地址:https://www.cnblogs.com/zyfzyf/p/4059898.html
Copyright © 2020-2023  润新知