题目来源:
HihoCoder1342
题目描述:
Let's draw a picture of full binary tree using ASCII characters. In this picture nodes are represented by '#'. A parent node connects its left child by '/' and its right child by '\'.
题目描述:
For the sake of aesthetic, the nodes of the same height must be painted on the same line. And the nodes must be perfectly connected by '/' and '\'. Intersections or misplacements are not allowed.
For example, this is the full binary tree of height 2:
This is the full binary tree of height 3:
This is the full binary tree of height 4:
Now we build a Cartesian coordinate system for the picture. We make the root at the origin (0, 0). The positive direction of x is to the bottom and the positive direction of y is to the right.
The full binary tree of height 2 is illustrated as below.
题目大意:利用'#'、'/'和'\'三种符号绘制指定层数的满二叉树,在绘制完成的图中定义二维坐标系,指定根节点的坐标为(0,0),然后给出一个矩形区域,判定矩形区域内包含多少个树的节点(即包含多少个#符号)。
解答:
本题题目描述的内容比较长,但解答的难度也不大。考察了一些计算和数学归纳的能力。为解本题,首先需要对程序绘制的图进行分析。
·图分析:
通过观察绘制的二叉树的图像,可以发现:整个图像是一个对称的图形。并且如果不考虑字符的不同,图像中每一行的宽度都比前一行多2,整个图形呈阶梯状分布:
D = 1 + 2×(H-1)..............................(1)
下面我们推导对于高度绘制高度为h的满二叉树的图像所需的行数,可以将其称为图像的“高度”,即为:H(h)。显然,H(1) = 1,H(2)=3。
图的高度:
对于高度为h的二叉树的图像的绘制,我们可以理解为下面的过程,绘制两个高度为h-1的满二叉树的图像,将其并排放置后,用一个公共的根节点将两棵子树连接起来,此时两棵子树的最后一层中间有1个字符的间隔:
D(h) = 2×D(h-1) + 1..........................(2)
根据式(1),可以计算D(h-1):
1 + 2×[H(h)-1] = 2×{1 + 2×[H(h-1) - 1]} + 1
=> H(h) - 1 = 2×[H(h-1) - 1] + 1
=> H(h) = 2×[H(h-1) - 1] + 2 = 2×H(h-1)
=> H(h) = 2×H(h-1)
此时,根据等比数列的计算公式可以得到:
D(h-1) = 1 + 2×[H(h-1) - 1]...................(3)
同时也可以根据式(1)计算D(h):
D(h) = 1 + 2×[H(h)-1].........................(4)
将式(3)和式(4)代入式(2),然后整理可得H(h)的递推计算公式:1 + 2×[H(h)-1] = 2×{1 + 2×[H(h-1) - 1]} + 1
=> H(h) - 1 = 2×[H(h-1) - 1] + 1
=> H(h) = 2×[H(h-1) - 1] + 2 = 2×H(h-1)
=> H(h) = 2×H(h-1)
此时,根据等比数列的计算公式可以得到:
H(h) = H(2)×2^(h-2)............................(5)
H(2)=3, 于是:
H(h) = 3 × 2^(h-2).............................(6)
以上就是计算图高度的公式。这里H(1)=1是一种特殊情况,不可以采用上面的公式计算。
各层节点竖直方向的坐标:
知道图的高度后,我们还需要知道图中各层节点在竖直方向上的坐标,题目中定义的坐标系竖直方向是X轴,定义根节点的深度是0,从上到下各层节点深度为1、2.....h-1。我们将图像中深度为x的节点的竖直方向坐标记为:X(x)。
根据题意根节点的坐标为(0,0),所以:X(0) = 0。对于高度为h的树,它的左右孩子都是高度为h-1的子树,而根节点左右孩子的都是深度为1的节点。
而高度为h-1的子树的图像高度为H(h-1),因此根节点到其左右孩子在竖直方向上的间距就是:H(h) - H(h-1) 即:
各层节点竖直方向的坐标:
知道图的高度后,我们还需要知道图中各层节点在竖直方向上的坐标,题目中定义的坐标系竖直方向是X轴,定义根节点的深度是0,从上到下各层节点深度为1、2.....h-1。我们将图像中深度为x的节点的竖直方向坐标记为:X(x)。
根据题意根节点的坐标为(0,0),所以:X(0) = 0。对于高度为h的树,它的左右孩子都是高度为h-1的子树,而根节点左右孩子的都是深度为1的节点。
X(1) - X(0) = H(h) - H(h-1)
根据上文中式(5),可以得到:
X(1) - X(0) = H(h) - H(h)/2 = H(h)/2
以此类推,可以得到:
X(x) - X(0)
= H(h)/2 + H(h)/(2^2) + H(h)/(2^3) + ... + H(h)/(2^x)
= {H(h)/2 × [1 - 1/(2^x)]} ÷ [1 - 1/2]
= H(h) - H(h)/(2^x).........................(7)
这样我们就求得了每层节点的X坐标。但该公式对于最后一层的结果却不适用,最后一行的结果需要特别处理。
各层节点水平坐标的最小值和最大值:
然后我们还需要知道的是每层节点水平坐标的最大值和最小值。这个值的求解方式是显而易见的。在我们得知X(x)之后,就可以利用式(1)来求得该层的宽度为:1 + 2 × X(x),而我们知道图像是左右对称的,根节点的水平坐标是0。所以:每一层节点水平坐标的最大值和最小值是互为相反数的。因此,我们只要记录其绝对值即可,记为:Y(x),那么:
各层节点的间距:
通过观察图像可以发现:除去最后一层的节点外,其余各层的节点间距都是相同的:
对于每一层节点的个数,是很容易计算的:深度为x的层,节点个数就是2^x个,因此如果求得节点间的间距,就可以求得树中每一个节点的精确坐标,而该间距的计算也是很容易的,深度为x的层宽度为:1 + 2×X(x) 而节点个数为2^x个,因此间距大小为:
总结:
下面是对于上面计算结果的总结,我们共用到了4条公式:
图高度: H(h) = 3 × 2^(h-2) (h>1)
各层节点的X坐标: X(x) = H(h) - H(h)/(2^x) (0≤x<H(h)-1)
各层节点Y坐标绝对值的最大值: Y(x) = X(x) (0≤x<H(h)-1)
各层节点的间隔: G(x) = [2X(x) + 1 - 2^x] / (2^x - 1) (0≤x<H(h)-1)
其中:h为树的高度;x为节点的深度,根节点深度为0,其余从上到下递增。
对于 h = 1 以及 x = H(h) - 1 的情况,上面的公式是不适用的,需要做特殊处理。
·计算:
在得到上面的公式后,对于题目结果的计算就比较容易了。题目给出了树的高度h和矩形范围的左上角和右下角的坐标(x1,y1)、(x2,y2)。首先根据给定个树高度h,求出H(h),然后对于深度x,依次赋值为0, 1......H(h)-1,检查X(x)是否落在了[x1,x2]范围内,如果是,则检查该层次的所有每一个节点是否落在了[y1,y2]范围内。如果是,则说明该点落在了矩形范围内,需要被计数。每个节点的坐标可以利用Y(x)和G(x)求得。
输入输出格式:
输入:
X(2) - X(1) = H(h-1)/2 = H(h) / (2^2)
X(3) - X(2) = H(h-2)/2 = H(h) / (2^3)
......
X(x) - X(x-1) = H(h-x+1)/2 = H(h) / (2^x)
累加以上各式,就可以得到:X(3) - X(2) = H(h-2)/2 = H(h) / (2^3)
......
X(x) - X(x-1) = H(h-x+1)/2 = H(h) / (2^x)
X(x) - X(0)
= H(h)/2 + H(h)/(2^2) + H(h)/(2^3) + ... + H(h)/(2^x)
= {H(h)/2 × [1 - 1/(2^x)]} ÷ [1 - 1/2]
= H(h) - H(h)/(2^x).........................(7)
这样我们就求得了每层节点的X坐标。但该公式对于最后一层的结果却不适用,最后一行的结果需要特别处理。
各层节点水平坐标的最小值和最大值:
然后我们还需要知道的是每层节点水平坐标的最大值和最小值。这个值的求解方式是显而易见的。在我们得知X(x)之后,就可以利用式(1)来求得该层的宽度为:1 + 2 × X(x),而我们知道图像是左右对称的,根节点的水平坐标是0。所以:每一层节点水平坐标的最大值和最小值是互为相反数的。因此,我们只要记录其绝对值即可,记为:Y(x),那么:
Y(x) = [1 + 2X(x) - 1] ÷ 2 = X(x).................(8)
此时,各层节点的水平坐标的最大值和最小值分别是 Y(x) 和 -Y(x)。各层节点的间距:
通过观察图像可以发现:除去最后一层的节点外,其余各层的节点间距都是相同的:
G(x) = {2 × X(x) + 1 - 2^x} ÷ (2^x - 1)...................(9)
总结:
下面是对于上面计算结果的总结,我们共用到了4条公式:
图高度: H(h) = 3 × 2^(h-2) (h>1)
各层节点的X坐标: X(x) = H(h) - H(h)/(2^x) (0≤x<H(h)-1)
各层节点Y坐标绝对值的最大值: Y(x) = X(x) (0≤x<H(h)-1)
各层节点的间隔: G(x) = [2X(x) + 1 - 2^x] / (2^x - 1) (0≤x<H(h)-1)
其中:h为树的高度;x为节点的深度,根节点深度为0,其余从上到下递增。
对于 h = 1 以及 x = H(h) - 1 的情况,上面的公式是不适用的,需要做特殊处理。
·计算:
在得到上面的公式后,对于题目结果的计算就比较容易了。题目给出了树的高度h和矩形范围的左上角和右下角的坐标(x1,y1)、(x2,y2)。首先根据给定个树高度h,求出H(h),然后对于深度x,依次赋值为0, 1......H(h)-1,检查X(x)是否落在了[x1,x2]范围内,如果是,则检查该层次的所有每一个节点是否落在了[y1,y2]范围内。如果是,则说明该点落在了矩形范围内,需要被计数。每个节点的坐标可以利用Y(x)和G(x)求得。
输入输出格式:
输入:
The first line contains two integers N and M. N is the height of the full binary tree. M is the number of queries. (1 ≤ N ≤ 20, 1 ≤ M ≤ 100)Each query consists of 4 integers x1, y1, x2 and y2. (x1, y1) is the left-top corner of the area and (x2,y2) is the right-bottom corner of the area.
输出:
For each query output the amount of nodes in the area.
程序代码:
For each query output the amount of nodes in the area.
程序代码:
/****************************************************/
/* File : HihoCoder1342 */
/* Author : Zhang Yufei */
/* Date : 2016-07-21 */
/* Description : HihoCoder ACM program. (submit:g++)*/
/****************************************************/
/*
* Formulas used in this program:
* H(h) = floor[3 * 2^(h-2)]
* X(x) = H(h) - floor[H(h)/2^x]
* W(x) = X(x)
* G(x) = [2*X(x) + 1 - 2^x] / (2^x - 1)
*/
#include<stdio.h>
#include<math.h>
// Input data.
int N, M;
/*
* This function computes how many node are in the give rectangle area.
* Parameters:
* @x1 & @y1: The left-top coordinates of the rectangle area.
* @x2 & @y2: The right-bottom coordinates of the rectangle area.
* Returns:
* The number of node which are in the given rectangle area.
*/
int compute(int x1, int y1, int x2, int y2) {
int H = (int) floor(3 * pow(2, N - 2));
int count = 0;
for(int i = 0; i < N; i++) {
int X = H - (int) floor(H / pow(2, i));
if(X >= x1 && X <= x2) {
if(i < N - 1) {
int G = (int) ((2 * X + 1 - pow(2, i)) / (pow(2, i) - 1)) + 1;
count += (int) pow(2, i);
if(y1 > -1 * X) {
count -= (y1 + X - 1) / G + 1;
}
if(y2 < X) {
count -= (X - y2 - 1) / G + 1;
}
} else {
count += (int) pow(2, i);
if(y1 > -1 * X) {
count -= (y1 + X - 1) / 6 * 2 + 1;
if((y1 + X - 1) % 6 >= 4) {
count -= 1;
}
}
if(y2 < X) {
count -= (X - y2 - 1) / 6 * 2 + 1;
if((X - y2 - 1) % 6 >= 4) {
count -= 1;
}
}
}
}
}
return count;
}
/*
* The main program.
*/
int main(void) {
scanf("%d %d", &N, &M);
for(int i = 0; i < M; i++) {
int x1, y1, x2, y2;
scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
printf("%d\n", compute(x1, y1, x2, y2));
}
return 0;
}