题目描述:
共 l 组数据。
已知一棵二叉树的深度为 D,每个节点和树叶都有一个值,初始时全为 F。放入任意一个小球时,都从根节点放进去。如果该节点的值为 F,小球就落入左子树;如果该节点的值为 T,小球就落入右子树。对接下来每个节点的值,也是一样的情况。直到落到树叶为止。
对每组数据,输入两个数 D 和 I ,要求输出第 I 个小球最后落下的位置。2 ≤ D ≤ 20,1 ≤ I ≤ 524 288 。
算法:
如果直接模拟,一定会超时。因为 2 ≤ D ≤ 20,1 ≤ I ≤ 524 288 。如果树有 20 层,就有 524 287 个节点。如果 I 比较大,如 524 288,就需要模拟 524 288 次下落,每次下落都需要 19 次判断并修改对应节点的值。显然,这将花费巨量的时间。
观察发现,无论是对根节点还是中间节点,第 I 个小球落至该节点时,如果 I 是奇数,则这个小球是第 (I + 1) / 2 个落入左子树的小球。如果 I 是偶数,则这个小球是第 I / 2 个落入右子树的小球。
于是我们有下述算法:
已知:如果二叉树的某个节点编号为 k ,则其左子节点的编号为 2k ,右子节点的编号为 2k + 1 。
从第一层开始一直计算到 (D - 1) 层。用一个变量 r 记录当前所在节点。在每一层,如果 I 是奇数,则将 I 修改为 (I + 1) / 2,并将小球落入左边,即 r 变为 2r ;如果 I 是偶数,则将 I 修改为 I / 2,并将小球落入右边,即 r 变为 2r + 1 。
计算完毕后,输出结果 r 。
例:
一个 5 层的二叉树,放入小球的编号为 31。
第 1 层:31 % 2 = 1,(31 + 1)/ 2 = 16,落入左子树,当前节点 r = 2 × 1 = 2。
第 2 层:16 % 2 = 0,16 / 2 = 8,落入右子树,当前节点 r = 2 × 2 + 1 = 5。
第 3 层:8 % 2 = 0,8 / 2 = 4,落入右子树,当前节点 r = 2 × 5 + 1 = 11。
第 4 层:4 % 2 = 0,4 / 2 = 2,落入右子树,当前节点 r = 2 × 11 + 2 = 23。
输出 23 。
AC代码(约 10 ~ 20 ms):
#include<cstdio>
#pragma warning(disable:4996)
unsigned int l, I, r; unsigned char D;
int main() {
scanf("%u", &l);
for (unsigned int i = 0; i < l; ++i) {
scanf("%hhu%u", &D, &I); r = 1;
for (unsigned char j = 1; j < D; ++j) {
switch (I % 2) {
case 1:I = (I + 1) / 2; r *= 2; break;
case 0:I /= 2; r = 2 * r + 1;
}
}
printf("%u\n", r);
}
return 0;
}
注:根据 https://www.cnblogs.com/tyty-Somnuspoppy/p/6260281.html 的代码,I % 2 可以替换为位运算 I & 1 来判断 I 的奇偶。
4460



被折叠的 条评论
为什么被折叠?



