• [luogu p1185] 绘制二叉树


    传送门

    绘制二叉树

    题目描述

    二叉树是一种基本的数据结构,它要么为空,要么由根节点,左子树和右子树组成,同时左子树和右子树也分别是二叉树。

    当一颗二叉树高度为(m-1)时,则共有(m)层。除(m)层外,其他各层的结点数都达到最大,且结点节点都在第(m)层时,这就是一个满二叉树。

    现在,需要你用程序来绘制一棵二叉树,它由一颗满二叉树去掉若干结点而成。对于一颗满二叉树,我们需要按照以下要求绘制:

    1、结点用小写字母"o"表示,对于一个父亲结点,用"/"连接左子树,同样用""连接右子树。

    2、定义([i,j)]为位于第(i)行第(j)列的某个字符。若([i,j])为"/",那么([i-1,j+1])([i+1,j-1])要么为"o",要么为"/"。若([i,j])为"",那么([i-1,j-1])([i+1,j+1])要么为"o",要么为""。同样,若([i,j])为第(1-m)层的某个节点(即"o"),那么([i+1,j-1])为"/",([i+1,j+1])为""。

    3、对于第(m)层节点也就是叶子结点,若两个属于同一个父亲,那么它们之间(由3)个空格隔开,若两个结点相邻但不属于同一个父亲,那么它们之间由(1)个空格隔开。第(m)层左数第(1)个节点之前没有空格。

    最后需要在一颗绘制好的满二叉树上删除(n)个结点(包括它的左右子树,以及与父亲的连接),原有的字符用空格替换(ASCII 32,请注意空格与ASCII 0的区别(若用记事本打开看起来是一样的,但是评测时会被算作错误答案!))。

    输入输出格式

    输入格式

    (1)行包含(2)个正整数(m)(n),为需要绘制的二叉树层数已经从(m)层满二叉树中删除的结点数。

    接下来(n)行,每行两个正整数,表示第(i)层第(j)个结点需要被删除($1

    输出格式

    按照题目要求绘制的二叉树。

    输入输出样例

    输入样例 #1

    2 0
    

    输出样例 #1

      o
     / 
    o   o
    

    输入样例 #2

    4 0
    

    输出样例 #2

               o
              / 
             /   
            /     
           /       
          /         
         o           o
        /          / 
       /          /   
      o     o     o     o
     /    /    /    / 
    o   o o   o o   o o   o
    

    输入样例 #3

    4 3
    3 2
    4 1
    3 4
    

    输出样例 #3

               o
              / 
             /   
            /     
           /       
          /         
         o           o
        /           /
       /           /
      o           o
                / 
        o       o   o
    

    说明

    (30\%)的数据满足:(n=0)

    (50\%)的数据满足:(2≤m≤5)

    (100\%)的数据满足:(2≤m≤10,0≤n≤10)

    分析

    此题是一道比较有质量的题。

    一开始写的分治,结果输出非常鬼畜,调了好几天都无果,无奈,参照了题解中的方法直接写的。

    我参照的是 KHIN 神的这篇题解。此篇题解几乎和KHIN神的一模一样。

    首先,定义 (r_i) (root) 为 (m = i) 时根节点的位置(从 (0) 计数),那么就会有

    [r_i = egin{cases} 0 & i = 1 \ 2 & i = 2 \ 2r_{i - 1} + 1 & i > 2end{cases} ]

    我们可以考虑简化一下这个式子。首先,找规律能推出,(forall 1 le k le i - 2),有

    [r_i = 2^kr_{i - k} + 2 ^ k - 1 ]

    (k = i - 2),则有

    [egin{aligned}r_i &= 2^{i - 2}r_2 + 2 ^ {i - 2} - 1 \ &= 2 ^ {i - 2} imes 2 + 2 ^ {i - 2} - 1 \ &= 2 ^ {i - 1} + 2 ^ { i - 2} - 1 \ &= 2^i - 2 ^ {i - 2} - 1end{aligned} ]

    也就是说,(forall i ge 2),有 (r_i = 2^i - 2 ^ {i - 2} - 1)。不知道你是否有发现,这个东西恰好是一层中的宽度(也就是该层节点个数)。

    那么,每个点到父亲节点的距离是什么呢?不难发现,该距离恰好跳过了该点和父节点间子树的宽度,跟 (r_i) 其实是一个东西。也就是说:

    定义 (e_i) (edge)一条 下方有 (i) 个节点的边,该边长度应为:

    [e_i = egin{cases} 1 & i = 1 \ r_i & i > 1 end{cases} ]

    不考虑删除,每次记录输出时每行边,点的位置,输出后,判断子树方向,若左子树则位置自减,右子树则位置自增。节点判断,更新数组。(本篇题解运用的是滚动数组,滚动输出。)

    而一个节点或边的删除与否,我们直接用一个 isErased 数组记录。从上向下逐个扫描,如果父节点被检测到删除或者本身这个节点就被删除了,那么就可以isErased数组记录。输出时,如果这个位置被擦除了,那么直接输出空格即可。

    上代码咯。

    代码

    /*
     * @Author: crab-in-the-northeast 
     * @Date: 2020-08-14 15:00:06 
     * @Last Modified by: crab-in-the-northeast
     * @Last Modified time: 2020-08-14 22:10:56
     */
    
    //之前写的解法太辣鸡,且死活查不出错,这里参照了类似于KHIN神的方法。
    //由神仙xk帮忙debug,并不是抄袭。qwq
    //链接:https://www.luogu.com.cn/blog/236807/Solution-luogu-P1185
    //顺便 % 一下KHIN神
    #include <iostream>
    #include <cstdio>
    
    const int maxn = 12;
    const int maxm = 12;
    
    bool isErased[maxm][1 << maxm];
    int pos[2][1 << maxm];
    
    int main() {
        int n, m;
        std :: scanf("%d%d", &m, &n);
        
        pos[0][0] = (1 << m) - (1 << m - 2);
    
        for (int i = 0; i < n; ++i) {
            int x, y;
            std :: scanf("%d%d", &x, &y);
            isErased[x - 1][y - 1] = true;
        }
    
        for (int i = 1; i < m; ++i)
            for (int j = 0; j < 1 << i; ++j)
                if(isErased[i - 1][j >> 1])
                    isErased[i][j] = true;
    
        for (int i = 1; i < pos[0][0]; ++i, putc(' ', stdout));
        puts("o");
        for (int i = 1; i < m; ++i) {
            for (int j = 0; j < 1 << i; ++j)
                pos[1][j] = pos[0][j >> 1] + (j & 1 ? 1 : -1);
            std :: swap(pos[0], pos[1]);
    
            for (int k = 1; k < std :: max((1 << m - i) - (1 << m - i - 2), 2) ; ++k) {
                for (int j = 1, l = 0; l < 1 << i; ++j)
                    if (j == pos[0][l]) {
                        putchar(isErased[i][l] ? ' ' : (l & 1 ? '\' : '/'));
                        pos[0][l] += l & 1 ? 1 : -1;
                        ++l;
                    } else
                        putchar(' ');
                puts("");
            }
    
            for (int j = 1, k = 0; k < 1 << i; ++j)
                putchar(j == pos[0][k] && !isErased[i][k++] ? 'o' : ' ');
            puts("");
        }
        return 0;
    }
    

    评测记录

    评测记录

  • 相关阅读:
    is running beyond physical memory limits. Current usage: 2.0 GB of 2 GB physical memory used; 2.6 GB of 40 GB virtual memory used
    saiku执行速度优化二
    saiku执行速度慢
    saiku 升级&备份&恢复
    saiku 展示优化第二步(要诀和技巧)
    saiku 无密码登陆
    saiku 展示优化
    saiku源代码安装
    结合使用saiku、mondrian workbentch建立多维查询报表
    浅析 mondrian 模式文件 Schema
  • 原文地址:https://www.cnblogs.com/crab-in-the-northeast/p/luogu-p1185.html
Copyright © 2020-2023  润新知