天天看点

UVA 679 Dropping Balls(二叉树性质)

​​Dropping Balls​​

题意,有一颗完全二叉树,有好多球从根节点往下走,关于往左还是往右要看每个节点的开关,初始的话全部关闭,每走过一个节点,这个节点开关会改变一次,问最后一个球落在哪个叶子节点,给出编号(开关开往右,反之往左)

这里有几个知识点

1.写法等同与 n * 2m

2. 因为

优先级比

高,所以n * 2m+1要写为(n << m) + 1

3. 对于完全二叉树,每个节点

的左右节点为

第一眼看的话很容易写出模拟版本的,模拟每一个小球,但是会超时

#include<bits/stdc++.h>
using namespace std;
const int N = 20;

int _, n, m;
int a[1<<N];    //1<<N即为1*2^n
          //最大节点数为2^n-1
int main()
{
  for (scanf("%d", &_); _; _--){
    cin >> n >> m;
    int maxn = (1 << n) - 1;      //最大节点数
    // cout << maxn << endl;
    memset(a, 0, sizeof(a));    //开关刚开始的时候为关闭,0
    int start;
    for(int i = 0; i < m; i++){    //连续让m个小球下落
      start = 1;
      while(start <= maxn){      //落出界后退出
        start = a[start] ? (start << 1) + 1 : start << 1; //选择方向
        a[start>>1] = !a[start>>1];             //改变开关
      }
    }
    // cout << start << endl;
    cout << (start >> 1) << endl; //输出节点start/2
  }
  cin >> _;
  return 0;
}      

仔细思考下就会发现,对于每一个节点只要知道当前小球是第奇数个还是偶数个到达这个节点的,就可以判断往左走还是右。

  • 1 >>>> 左
  • 2 >>>> 右
  • 3 >>>> 左
  • … …

知道往左还是往右,就推出来是第几个到达子树了

  • 第D次到达父节点
  • 往左 >>>> 第
  • 次到达左子树
  • 往右 >>>> 第
  • 次到达右子树

那么我们就可以直接模拟最后的小球的路径了

#include<bits/stdc++.h>
using namespace std;
const int N = 20;

int _, n, m;
int main()
{
  for (scanf("%d", &_); _;_--){
    cin >> n >> m;
    int ans = 1;        //节点的编号
    for(int i = 0; i < n-1; i++){  //小球下降了n-1次,
                    //第m个小球是第m个到达1节点的
      if(m&1){        //n为奇数
        ans = ans << 1;   //往左
        m = (m + 1) / 2;  //最后一个小球到达左子树是第几个到达的
      }else{  
        ans = (ans << 1) + 1; //往右
        m = m / 2;      //最后一个小球到达右子树是第几个到达的
      }
    }
    cout << ans << endl;
  }
  cin >> n;
  return 0;
}      

继续阅读